├── AWS_ASG_EnterStandby.ps1
├── AWS_ASG_ExitStandby.ps1
├── AzureDevOps_Get-ReleasesToPromote.ps1
├── Copy-AzDevOpsBuildDefinition.ps1
├── Delete-AzureRepos.ps1
├── GAC_RegisterAssemblies.ps1
├── GenerateAzureSASKey.ps1
├── Get-InstalledSoftware.ps1
├── Get-IssuesWorkCompleted.ps1
├── Get-UpdatedAppForAzDevOpsBuild.ps1
├── IIS_Deploy.psm1
├── IIS_SetPropertiesForFasterStartup.ps1
├── JBoss_FTPUploadLogs.ps1
├── O365_UndeliverableMessageExport.ps1
├── Octopus_UpdateVariable.ps1
├── README.md
├── SSIS_DeployISPAC.ps1
├── TFS_AggregateCodeCoverageResults.ps1
├── TFS_CreateBranchPolicies.ps1
├── TFS_GetCommitAssociatedToBuild.ps1
├── TFS_PauseBuildsPerEnvironment.ps1
├── TFS_UpdateWorkItemsWithBuildLink.ps1
├── Update-AzDevOpsBuildAgentPools-.ps1
├── Update-AzDevOpsReleaseAgentPools.ps1
├── Update-ReleaseDefinitionsWindowsMachineFileCopyTask.ps1
└── VSTS_CreateReleasePullRequest.ps1
/AWS_ASG_EnterStandby.ps1:
--------------------------------------------------------------------------------
1 | # Part 1 of 2
2 | # Part 1 Places EC2 instance into autoscaling group's standby mode.
3 | # Part 2 Exits standby mode and waits for instance to be InService.
4 | param (
5 | [Parameter(Mandatory=$true)][string]$ASGNameVariable # Passed in deploy step, example: WebASGName.
6 | )
7 | # Get EC2 Instance
8 | Try
9 | {
10 | $response = Invoke-RestMethod -Uri "http://169.254.169.254/latest/meta-data/instance-id" -Method Get
11 | If ($response)
12 | {
13 | $instanceId = $response
14 | }
15 | Else
16 | {
17 | Write-Error -Message "Returned Instance ID does not appear to be valid"
18 | Exit 1
19 | }
20 | }
21 | Catch
22 | {
23 | Write-Error -Message "Failed to load instance ID from AWS." -Exception $_.Exception
24 | Exit 1
25 | }
26 |
27 | # Get Stack name and status
28 | # If Stack is being Updated, instances are updated via AutoScaling group policy,
29 | # and there is no need to place instances into StandBy
30 | Try
31 | {
32 | $stackName = Get-EC2Tag -Filter @{ Name="key";Values="aws:cloudformation:stack-name"},@{ Name="resource-id";Values=$instanceID}
33 | $stackInfo = Get-CFNStack -StackName $stackName.Value
34 |
35 | if($stackInfo.StackStatus -eq "UPDATE_IN_PROGRESS"){
36 | Write-Host "CloudFormation stack updating, this Octopus step will now be skipped."
37 | Exit
38 | }
39 | }
40 | Catch
41 | {
42 | Write-Error -Message "Failed to retrieve CloudFormation stack status from AWS." -Exception $_.Exception
43 | Exit 1
44 | }
45 |
46 | # Get ASG Name using $instanceId and set Octopus output variable to be used in subsequent deploy step AWS_ASG_ExitStandby.ps1
47 | Try
48 | {
49 | $ASGInfo = Get-ASAutoScalingInstance -InstanceId $instanceId
50 | $ASGName = $ASGInfo.AutoScalingGroupName
51 |
52 | If ($ASGName)
53 | {
54 | # Set ASGNameVariable Octopus output variable passed as argument in deploy step (1 ASGNameVariable per server type)
55 | # Referenced in subsequent deploy step AWS_ASG_ExitStandby.ps1: $ASGNameVariable = $OctopusParameters["Octopus.Action[AWS_ASG_EnterStandby.ps1].Output.$ASGNameVariable"]
56 | Write-Host "Setting Octopus output variable $ASGNameVariable to value: $ASGName"
57 | Set-OctopusVariable -name "$ASGNameVariable" -value "$ASGName"
58 | Write-Host "Output variable set."
59 | }
60 | Else
61 | {
62 | Write-Error -Message "Returned Auto Scaling Group name does not appear to be valid"
63 | Exit 1
64 | }
65 | }
66 | Catch
67 | {
68 | Write-Error -Message "Failed to retrieve Auto Scaling Group name from AWS." -Exception $_.Exception
69 | Exit 1
70 | }
71 |
72 | # Place instance in standby mode if InService, skip if already in standby mode.
73 | Try
74 | {
75 | $instanceState = (Get-ASAutoScalingInstance -InstanceId $instanceId).LifecycleState
76 |
77 | If($instanceState -eq "InService")
78 | {
79 | Write-Host "Placing instance: $instanceId into standby mode for ASG: $ASGName"
80 | Enter-ASStandby -InstanceId $instanceId -AutoScalingGroupName $ASGName -ShouldDecrementDesiredCapacity $true -Force
81 | Write-Host "Instance $instanceId is now in standby mode"
82 | }
83 | ElseIf($instanceState -eq "Standby")
84 | {
85 | Write-Host "Instance already in standby"
86 | }
87 | Else
88 | {
89 | Write-Error -Message "Error: Instance is not InService or Standby mode." -Exception $_.Exception
90 | Exit 1
91 | }
92 | }
93 | Catch
94 | {
95 | Write-Error -Message "Failed to place instance in standby mode." -Exception $_.Exception
96 | Exit 1
97 | }
98 |
--------------------------------------------------------------------------------
/AWS_ASG_ExitStandby.ps1:
--------------------------------------------------------------------------------
1 | # Part 2 of 2
2 | # Part 1 Places EC2 instance into autoscaling group's standby mode.
3 | # Part 2 Exits standby mode and waits for instance to be InService.
4 |
5 | param (
6 | [Parameter(Mandatory=$true)][string]$ASGEnterStandbyDeployStep, # Deploy step name of AWS_ASG_EnterStandby.ps1
7 | [Parameter(Mandatory=$true)][string]$ASGNameVariable, # Variable name that is set by AWS_ASG_EnterStandby.ps1 for ASG Name
8 | [Parameter(Mandatory=$true)][string]$registrationCheckInterval,
9 | [Parameter(Mandatory=$true)][string]$maxRegistrationCheckCount
10 | )
11 |
12 | # Get EC2 Instance
13 | Try
14 | {
15 | $response = Invoke-RestMethod -Uri "http://169.254.169.254/latest/meta-data/instance-id" -Method Get
16 | If ($response)
17 | {
18 | $instanceId = $response
19 | Write-Host "Instance ID: $instanceId"
20 | }
21 | Else
22 | {
23 | Write-Error -Message "Returned Instance ID does not appear to be valid"
24 | Exit 1
25 | }
26 | }
27 | Catch
28 | {
29 | Write-Error -Message "Failed to load instance ID from AWS." -Exception $_.Exception
30 | Exit 1
31 | }
32 |
33 | # Get Stack name and status
34 | # If Stack is being Updated, instances are updated via AutoScaling group policy,
35 | # and there is no need to place instances into StandBy
36 | Try
37 | {
38 |
39 | $stackName = Get-EC2Tag -Filter @{ Name="key";Values="aws:cloudformation:stack-name"},@{ Name="resource-id";Values=$instanceID}
40 | $stackInfo = Get-CFNStack -StackName $stackName.Value
41 |
42 | if($stackInfo.StackStatus -eq "UPDATE_IN_PROGRESS"){
43 | Write-Host "CloudFormation stack updating, this Octopus step will now be skipped."
44 | Exit
45 | }
46 | }
47 | Catch
48 | {
49 | Write-Error -Message "Failed to retrieve CloudFormation stack status from AWS." -Exception $_.Exception
50 | Exit 1
51 | }
52 |
53 | # Get ASG Name variable from previous deploy step (AWS_ASG_EnterStandby.ps1)
54 | Try
55 | {
56 | $ASGName = $OctopusParameters["Octopus.Action[$ASGEnterStandbyDeployStep].Output.$ASGNameVariable"]
57 | Write-Host "Auto Scaling Group Name: $ASGName"
58 | If (!$ASGName)
59 | {
60 | Write-Error -Message "Returned Auto Scaling Group Name does not appear to be valid"
61 | Exit 1
62 | }
63 | }
64 | Catch
65 | {
66 | Write-Error -Message "Failed to get ASGNameVariable output variable from Octopus" -Exception $_.Exception
67 | Exit 1
68 | }
69 |
70 | # Exit standby mode
71 | Try
72 | {
73 |
74 | Write-Host "Exiting standby mode for instance: $instanceId in ASG: $ASGName."
75 | Exit-ASStandby -InstanceId $instanceId -AutoScalingGroupName $ASGName -Force
76 | Write-Host "Instance exited standby mode, waiting for it to go into service."
77 |
78 | $instanceState = (Get-ASAutoScalingInstance -InstanceId $instanceId).LifecycleState
79 | Write-Host "Current State: $instanceState"
80 |
81 | $checkCount = 0
82 |
83 | Write-Host "Retry Parameters:"
84 | Write-Host "Maximum Checks: $maxRegistrationCheckCount"
85 | Write-Host "Check Interval: $registrationCheckInterval"
86 |
87 | While ($instanceState -ne "InService" -and $checkCount -le $maxRegistrationCheckCount)
88 | {
89 | $checkCount += 1
90 |
91 | # Wait a bit until we check the status
92 | Write-Host "Waiting for $registrationCheckInterval seconds for instance to be InService"
93 | Start-Sleep -Seconds $registrationCheckInterval
94 |
95 | If ($checkCount -le $maxRegistrationCheckCount)
96 | {
97 | Write-Host "$checkCount/$maxRegistrationCheckCount Attempts"
98 | }
99 | $instanceState = (Get-ASAutoScalingInstance -InstanceId $instanceId).LifecycleState
100 | Write-Host "Current instance state: $instanceState"
101 | }
102 |
103 | If ($instanceState -eq "InService")
104 | {
105 | Write-Host "Instance in service!"
106 | }
107 | Else
108 | {
109 | Write-Error -Message "Instance not in service: $instanceState"
110 | Exit 1
111 | }
112 | }
113 | Catch
114 | {
115 | Write-Error -Message "Failed to exit standby mode." -Exception $_.Exception
116 | Exit 1
117 | }
118 |
--------------------------------------------------------------------------------
/AzureDevOps_Get-ReleasesToPromote.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .Description
3 | Script used to gather all releases that have been successfully deployed to QA,
4 | but not yet promoted to Pre-Prod or Prod environments.
5 |
6 | .Outputs
7 | "C:\Temp\LatestReleases.html"
8 | #>
9 |
10 | [CmdletBinding()]
11 | Param
12 | (
13 | [Parameter(Mandatory=$true)]
14 | $PAT, # Personal Access Token
15 | [Parameter(Mandatory=$false)]
16 | $TFSBaseURL
17 | )
18 | # Base64-encodes the Personal Access Token (PAT) appropriately
19 | # This is required to pass PAT through HTTP header
20 | $script:User = "" # Not needed when using PAT, can be set to anything
21 | $script:Base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $User,$PAT)))
22 |
23 | # Get list of all release definitions
24 | [uri] $script:GetDefinitionsUri = "$TFSBaseURL/_apis/Release/definitions"
25 |
26 | # Invoke the REST call and capture the response
27 | $GetDefinitionsUriResponse = Invoke-RestMethod -Uri $GetDefinitionsUri -Method Get -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)}
28 | $DefinitionIDs = $GetDefinitionsUriResponse.value.id
29 |
30 | # Create custom object to store output in, that can be used to build HTML report.
31 | $objTemplateObject = New-Object psobject
32 | $objTemplateObject | Add-Member -MemberType NoteProperty -Name DefinitionName -Value $null
33 | $objTemplateObject | Add-Member -MemberType NoteProperty -Name Link -Value $null
34 |
35 | # Create empty array which will become the output object
36 | $objResult = @()
37 |
38 | # Use definition ID's to loop and get latest deployments of each definition
39 | ForEach($DefinitionID in $DefinitionIDs){
40 | [uri] $GetLatestDeployments = "$TFSBaseURL/_apis/release/deployments?definitionId=" + $DefinitionID + "&api-version=4.0-preview&deploymentStatus=succeeded"
41 | $GetLatestDeploymentsResponse = Invoke-RestMethod -Uri $GetLatestDeployments -Method GET -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)}
42 |
43 | # Get successful deployments to QA
44 | $Deployments = ""
45 | $Deployments = $GetLatestDeploymentsResponse.value |
46 | Where-Object {$_.releaseEnvironment.name -like "QA*" -AND $_.deploymentStatus -eq "succeeded"}
47 |
48 | # Use first deployment ID in array to pick latest
49 | Try{
50 | $LatestDeployment = ""
51 | $LatestDeployment = $Deployments[0]
52 | }
53 | Catch{
54 | # Do nothing if null array
55 | }
56 |
57 | # Use Release ID to check if release is already deployed to Pre-Prod or Prod
58 | $ReleaseId = $LatestDeployment.release.id
59 | [uri] $GetRelease = "$TFSBaseURL/_apis/Release/releases/" + $ReleaseId + "?api-version=4.0-preview"
60 | $GetReleaseResponse = Invoke-RestMethod -Uri $GetRelease -Method GET -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)}
61 |
62 | # Get active releases only (not abandoned)
63 | $GetReleaseResponse = $GetReleaseResponse | Where-Object {$_.status -eq "active"}
64 |
65 | # Check if deployed to pre-prod or prod yet, and active (not abandoned)
66 | $NoDeployment = ""
67 | $NoDeployment = $GetReleaseResponse.environments | Where-Object {$_.name -like "*PROD*" -AND $_.status -eq "notStarted"}
68 | $NoDeploymentReleaseDefinitionName = ""
69 | $NoDeploymentReleaseDefinitionName = $NoDeployment.releaseDefinition.name
70 |
71 | If($NoDeployment){
72 | # Write output to Azure Pipeline log
73 | $NoDeploymentReleaseDefinitionName | Select-Object -first 1
74 | $LatestDeployment.release.webAccessUri
75 |
76 | # Create an instance of new object to prepare it with data and later add it to the result array for report
77 | $objTemp = $objTemplateObject | Select-Object *
78 |
79 | # Populate the custom object properties
80 | $objTemp.DefinitionName = $NoDeploymentReleaseDefinitionName | Select-Object -first 1
81 | $objTemp.Link = $LatestDeployment.release.webAccessUri
82 |
83 | # Add temp object to output array and get ready to loop back around
84 | $objResult += $objTemp
85 | }
86 | }
87 |
88 | # Set CSS properties for HTML report
89 | $Header = @"
90 |
95 | "@
96 |
97 | # Output to HTML file that is sent via email in release definition
98 | $objResult = $objResult |
99 | ConvertTo-Html @{Label="DefinitionName";Expression={$_.DefinitionName}},@{Label="Link";Expression={ "$($_.Link)" }} -Head $Header
100 | Add-Type -AssemblyName System.Web
101 | [System.Web.HttpUtility]::HtmlDecode($objResult) | Out-File "C:\Temp\LatestReleases.html"
102 |
--------------------------------------------------------------------------------
/Copy-AzDevOpsBuildDefinition.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding()]
2 | Param
3 | (
4 | [Parameter(Mandatory=$true)]
5 | $PAT, # Personal Access Token
6 | [Parameter(Mandatory=$true)]
7 | $DefinitionToCloneID, # ID of "Golden" build definition to clone.
8 | [Parameter(Mandatory=$true)]
9 | $LOB, # Line of business name. Used to reference Git repo source of build definition.
10 | [Parameter(Mandatory=$true)]
11 | $AzureDevOpsProjectURL # https://vsrm.dev.azure.com/{organization}/{project}
12 | )
13 |
14 | # Base64-encodes the Personal Access Token (PAT) appropriately
15 | # This is required to pass PAT through HTTP header
16 | $script:User = "" # Not needed when using PAT, can be set to anything
17 | $script:Base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $User,$PAT)))
18 |
19 | # Get Definition URI https://docs.microsoft.com/en-us/rest/api/azure/devops/build/definitions/get?view=azure-devops-rest-5.1
20 | [uri] $script:GetDefinitionUri = "$AzureDevOpsProjectURL/_apis/build/definitions/$DefinitionToCloneID`?api-version=5.0"
21 | $GetDefinitionResponse = Invoke-RestMethod -Uri $GetDefinitionUri -Method GET -ContentType "application/json" -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)}
22 |
23 | # Use response to form requst body for new definition
24 | $GetDefinitionResponse.name = "$LOB" # Set new definition name to name of LOB
25 | $GetDefinitionResponse.repository.name = "$LOB" # Set repo name and URL to LOB repo
26 | $GetDefinitionResponse.repository.url = "$AzureDevOpsProjectURL/_git/$LOB"
27 |
28 | # Convert response to JSON to be used in POST body below
29 | $ConvertResponseToRequestBody = $GetDefinitionResponse | ConvertTo-Json -Depth 10
30 |
31 | # Create Definition URI https://docs.microsoft.com/en-us/rest/api/azure/devops/build/definitions/create?view=azure-devops-server-rest-5.0
32 | [uri] $script:CreateDefinitionUri = "$AzureDevOpsProjectURL/_apis/build/definitions?definitionToCloneId=$DefinitionToCloneID&api-version=5.0"
33 |
34 | # Invoke the Create REST call and capture the response
35 | Invoke-RestMethod -Uri $CreateDefinitionUri -Method POST -Body $ConvertResponseToRequestBody -ContentType "application/json" -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)}
36 |
--------------------------------------------------------------------------------
/Delete-AzureRepos.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .Description
3 | Nukes all Git repos in an Azure DevOps Org.
4 | #>
5 |
6 | $PAT = "" # Personal Access Token
7 | $AzureDevOpsOrgURL = ""
8 |
9 | # Base64-encodes the Personal Access Token (PAT) appropriately
10 | # This is required to pass PAT through HTTP header
11 | $script:User = "" # Not needed when using PAT, can be set to anything
12 | $script:Base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $User,$PAT)))
13 |
14 | # URI to get list of all Projects in Org
15 | [uri] $script:GetProjectsURI = "$AzureDevOpsOrgURL/_apis/projects"
16 |
17 | # Get list of all Projects in Org
18 | $GetProjectsResponse = Invoke-RestMethod -Uri $GetProjectsURI -Method GET -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)}
19 | $Projects = $GetProjectsResponse.value.name
20 |
21 | # Loop through each project and get repo ID
22 | ForEach($Project in $Projects){
23 | # URI to get project repos
24 | [uri] $script:GetProjectReposURI = "$AzureDevOpsOrgURL/$Project/_apis/git/repositories?api-version=5.0"
25 | $GetProjectReposResponse = Invoke-RestMethod -Uri $GetProjectReposURI -Method GET -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)}
26 | $RepoIDs = $GetProjectReposResponse.value.id
27 |
28 | # Loop through each repo ID in project and DELETE
29 | ForEach($RepoID in $RepoIDs){
30 | If($RepoID){
31 | "Deleting repo from $Project"
32 | [uri] $script:DeleteRepoURI = "$AzureDevOpsOrgURL/$Project/_apis/git/repositories/$RepoID`?api-version=5.0"
33 | $DeleteRepoResponse = Invoke-RestMethod -Uri $DeleteRepoURI -Method DELETE -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)}
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/GAC_RegisterAssemblies.ps1:
--------------------------------------------------------------------------------
1 | # List of assmemblies to be GAC'd
2 | $assemblyDll = @('example.dll')
3 |
4 | # method for adding new assemblies to the GAC
5 | function Add-GacItem([string]$file) {
6 | Begin
7 | {
8 | # see if the Enterprise Services Namespace is registered
9 | if ($null -eq ([AppDomain]::CurrentDomain.GetAssemblies() |? { $_.FullName -eq "System.EnterpriseServices, Version=2.0.0.0, Culture=neutral, PublicKeyToken=$publicKeyToken" }) ) {
10 | # register the Enterprise Service .NET library
11 | [System.Reflection.Assembly]::Load("System.EnterpriseServices, Version=2.0.0.0, Culture=neutral, PublicKeyToken=$publicKeyToken") | Out-Null
12 | }
13 |
14 | # create a reference to the publish class
15 | $publish = New-Object System.EnterpriseServices.Internal.Publish
16 | }
17 | Process
18 | {
19 | # ensure the file that was provided exists
20 | if ( -not (Test-Path $file -type Leaf) ) {
21 | throw "The assembly '$file' does not exist."
22 | }
23 |
24 | # ensure the file is strongly signed before installing in the GAC
25 | if ( [System.Reflection.Assembly]::LoadFile( $file ).GetName().GetPublicKey().Length -eq 0) {
26 | throw "The assembly '$file' must be strongly signed."
27 | }
28 |
29 | # install the assembly in the GAC
30 | Write-Output "Installing: $assembly"
31 | $publish.GacInstall( $file )
32 | }
33 | }
34 |
35 | # method for removing assemblies from the GAC
36 | function Remove-GacItem([string]$file) {
37 | Begin
38 | {
39 | # see if the Enterprise Services Namespace is registered
40 | if ($null -eq ([AppDomain]::CurrentDomain.GetAssemblies() |? { $_.FullName -eq "System.EnterpriseServices, Version=2.0.0.0, Culture=neutral, PublicKeyToken=$publicKeyToken" }) ) {
41 | # register the Enterprise Service .NET library
42 | [System.Reflection.Assembly]::Load("System.EnterpriseServices, Version=2.0.0.0, Culture=neutral, PublicKeyToken=$publicKeyToken") | Out-Null
43 | }
44 |
45 | # create a reference to the publish class
46 | $publish = New-Object System.EnterpriseServices.Internal.Publish
47 | }
48 | Process
49 | {
50 | # ensure the file that was provided exists
51 | if ( -not (Test-Path $file -type Leaf) ) {
52 | throw "The assembly '$file' does not exist."
53 | }
54 |
55 | # ensure the file is strongly signed before installing in the GAC
56 | if ( [System.Reflection.Assembly]::LoadFile( $file ).GetName().GetPublicKey().Length -eq 0) {
57 | throw "The assembly '$file' must be strongly signed."
58 | }
59 |
60 | # install the assembly in the GAC
61 | Write-Output "UnInstalling: $file"
62 | $publish.GacRemove( $file )
63 | }
64 | }
65 |
66 | foreach($file in $assemblyDll)
67 | {
68 |
69 | Write-Host $file
70 | $currentDirectory = Get-Location
71 | $file = $currentDirectory.Path + "\" + $file
72 |
73 | Write-Host "UnRegistering the Assembly: '$file'"
74 | Remove-GacItem $file
75 |
76 | Write-Host "Registering the Assembly: '$file'"
77 | Add-GacItem $file
78 |
79 |
80 | }
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/GenerateAzureSASKey.ps1:
--------------------------------------------------------------------------------
1 | $storageAccountName = ""
2 | $storageAccountKey = ""
3 | $container = ""
4 |
5 | $context = New-AzureStorageContext -StorageAccountName $storageAccountName -StorageAccountKey $storageAccountKey
6 | $sas = New-AzureStorageContainerSASToken -Name $container -Permission rl -Context $context -ExpiryTime ([DateTime]::UtcNow.AddDays(7))
7 | Write-Host "$($context.BlobEndPoint)$($container)$($sas)"
8 |
--------------------------------------------------------------------------------
/Get-InstalledSoftware.ps1:
--------------------------------------------------------------------------------
1 | Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName, DisplayVersion, Publisher, InstallDate |
2 | Format-Table –AutoSize > D:\temp\installed_software.txt
--------------------------------------------------------------------------------
/Get-IssuesWorkCompleted.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .Description
3 | Script used to gather all Issues with linked tasks, then sum the amount of completed work in the linked tasks by A and B Teams.
4 |
5 | Gets list of Issues with tasks with shared query.
6 |
7 | .Outputs
8 | "C:\Temp\IssuesWorkCompleted.html" that is sent via email in release definition
9 | #>
10 |
11 | [CmdletBinding()]
12 | Param
13 | (
14 | [Parameter(Mandatory=$true)]
15 | $PAT, # Personal Access Token
16 | [Parameter(Mandatory=$false)]
17 | $AzureDevOpsBaseURL
18 | )
19 |
20 | # https://docs.microsoft.com/en-us/azure/devops/integrate/concepts/rest-api-versioning?view=azure-devops
21 | # Specify api version to prevent breaking changes after upgrdades
22 | $apiVersion = "3.0"
23 |
24 | # Base64-encodes the Personal Access Token (PAT) appropriately
25 | # This is required to pass PAT through HTTP header
26 | $script:User = "" # Not needed when using PAT, can be set to anything
27 | $script:Base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $User,$PAT)))
28 |
29 | # Create custom object to store output in, that can be used to build HTML report.
30 | $objTemplateObject = New-Object psobject
31 | $objTemplateObject | Add-Member -MemberType NoteProperty -Name WIID -Value $null
32 | $objTemplateObject | Add-Member -MemberType NoteProperty -Name WIName -Value $null
33 | $objTemplateObject | Add-Member -MemberType NoteProperty -Name WICreatedDate -Value $null
34 | $objTemplateObject | Add-Member -MemberType NoteProperty -Name WICreatedBy -Value $null
35 | $objTemplateObject | Add-Member -MemberType NoteProperty -Name WIClosedDate -Value $null
36 | $objTemplateObject | Add-Member -MemberType NoteProperty -Name WITeam -Value $null
37 | $objTemplateObject | Add-Member -MemberType NoteProperty -Name WIAreaPath -Value $null
38 | $objTemplateObject | Add-Member -MemberType NoteProperty -Name PercentA -Value $null
39 | $objTemplateObject | Add-Member -MemberType NoteProperty -Name PercentB -Value $null
40 | $objTemplateObject | Add-Member -MemberType NoteProperty -Name ATeam -Value $null
41 | $objTemplateObject | Add-Member -MemberType NoteProperty -Name BTeam -Value $null
42 |
43 | # Create empty array which will become the output object
44 | $objResult = @()
45 |
46 | # Get all work items using shared query "All Issues with Closed Tasks"
47 | [uri] $GetWorkItemQueryURI = "$AzureDevOpsBaseURL/_apis/wit/wiql/d32f77bd-2ed5-4c23-aac8-002294f34074" + "?api-version=$apiVersion"
48 | $GetWorkItemQueryResponse = Invoke-RestMethod -Uri $GetWorkItemQueryURI -Method GET -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)}
49 |
50 | # Get Issues
51 | $Issues = $GetWorkItemQueryResponse.workItemRelations.source.url
52 |
53 | # Get rid of dupes
54 | $Issues = $Issues | Select-Object -Unique
55 |
56 | # Use AzureDevOps Team "A" to identify A team members
57 | [uri] $GetTeamURI = "$AzureDevOpsBaseURL/_apis/projects/PROJECTNAME/teams/A/members" + "?api-version=$apiVersion"
58 | $GetTeamURIResponse = Invoke-RestMethod -Uri $GetTeamURI -Method GET -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)}
59 | $BTeamMembers = $GetTeamURIResponse.value.uniquename -replace "DOMAINNAME\\", ""
60 |
61 | ForEach($Issue in $Issues){
62 | $GetIssueWorkItemResponse = Invoke-RestMethod -Uri "$Issue`?api-version=$apiVersion`&`$expand=relations" -Method GET -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)}
63 |
64 | # Create an instance of new object to prepare it with data and later add it to the result array for report
65 | $objTemp = $objTemplateObject | Select-Object *
66 |
67 | # Get related tasks
68 | $relatedWorkItems = $GetIssueWorkItemResponse.relations | Where-Object {$_.rel -like "System.LinkTypes*" -OR $_.rel -like "Microsoft.VSTS*"}
69 | $relatedWorkItems = $relatedWorkItems.url
70 | If($relatedWorkItems){
71 | ForEach($workItem in $relatedWorkItems){
72 | $GetRelatedWorkItemResponseURI = $workItem + "?api-version=$apiVersion"
73 | $GetRelatedWorkItemResponse = Invoke-RestMethod -Uri $GetRelatedWorkItemResponseURI -Method GET -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)}
74 | If($GetRelatedWorkItemResponse.Fields.'System.WorkItemType' -eq "Task"){
75 | $relatedTask = $workItem
76 | $GetRelatedTaskURI = $relatedTask + "?api-version=$apiVersion"
77 | $GetRelatedTaskResponse = Invoke-RestMethod -Uri $GetRelatedTaskURI -Method GET -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)}
78 |
79 | # Figure out which team is assigned the task, then add completed hours
80 | $AssignedTo = $GetRelatedTaskResponse.fields.'System.AssignedTo'
81 |
82 | If(!($AssignedTo)){
83 | "Task not assigned"
84 | }
85 | Else{
86 | # Remove First, Last, and Domain name from AssignedTo, we only want username
87 | $AssignedTo = ($AssignedTo -split "\", 2, "simplematch")[1]
88 | $AssignedTo = $AssignedTo.TrimEnd(">")
89 |
90 | "Assigned To: " + $AssignedTo
91 | If($AssignedTo -in $BTeamMembers){
92 | $objTemp.BTeam += $GetRelatedTaskResponse.fields.'Microsoft.VSTS.Scheduling.CompletedWork'
93 | "A Team Hours: " + $objTemp.BTeam
94 | }
95 | Else{
96 | $objTemp.ATeam += $GetRelatedTaskResponse.fields.'Microsoft.VSTS.Scheduling.CompletedWork'
97 | "B Team Hours: " + $objTemp.ATeam
98 | }
99 | }
100 | }
101 | }
102 | # Only populate object if there is completed work > 0
103 | If($objTemp.ATeam -gt 0 -OR $objTemp.BTeam -gt 0){
104 | $objTemp.WIID = $GetIssueWorkItemResponse.id
105 | $objTemp.WIName = $GetIssueWorkItemResponse.fields.'System.Title'
106 | $objTemp.WIName = $objTemp.WIName | Select-Object -First 50
107 |
108 | $WICreatedDate = $GetIssueWorkItemResponse.fields.'System.CreatedDate'
109 | $objTemp.WICreatedDate = ($WICreatedDate -split "T", 2, "simplematch")[0]
110 | $objTemp.WICreatedBy = $GetIssueWorkItemResponse.fields.'System.CreatedBy'
111 | $WIClosedDate = $GetIssueWorkItemResponse.fields.'Microsoft.VSTS.Common.ClosedDate'
112 | $objTemp.WIClosedDate = ($WIClosedDate -split "T", 2, "simplematch")[0]
113 | $objTemp.WIAreaPath = $GetIssueWorkItemResponse.fields.'System.AreaPath'
114 | $objTemp.WITeam = $GetIssueWorkItemResponse.fields.'Custom.Team'
115 |
116 | # Find percentages between A and B
117 | $Total = $objTemp.ATeam + $objTemp.BTeam
118 | $objTemp.PercentA = ($objTemp.ATeam/$Total * 100)
119 | $objTemp.PercentB = ($objTemp.BTeam/$Total * 100)
120 | }
121 | }
122 | Else{
123 | "No related work items"
124 | continue # Move to next iteration in ForEach of Issues
125 | }
126 | # All report fields populated for Issue, add temp object to output array and get ready to loop back around
127 | $objResult += $objTemp
128 | }
129 |
130 | # Output Work Item ID, Work Item Name, Team, and Completed Work to 2nd fragment of HTML file
131 | $Fragment1 = $objResult |
132 | Select-Object -Property @{n="Issue Work Item ID";e={$_.WIID}},@{n="Issue Work Item Name";e={$_.WIName}},@{n="Created Date";e={$_.WICreatedDate}},@{n="Created By";e={$_.WICreatedBy}},@{n="Closed Date";e={$_.WIClosedDate}},@{n="Area Path";e={$_.WIAreaPath}},@{n="Team";e={$_.WITeam}},@{n="% A";e={$_.PercentA}},@{n="% B";e={$_.PercentB}},@{n="Total Hours A Team";e={$_.ATeam}},@{n="Total Hours B Team";e={$_.BTeam}} |
133 | Sort-Object -Property WIID -Descending |
134 | ConvertTo-Html -Fragment
135 |
136 | # Insert boostrap classes and required thead and tbody for sort
137 | $Fragment1 = $Fragment1 -replace '
',''
138 | $Fragment1 = $Fragment1 -replace '',''
139 | $Fragment1 = $Fragment1 -replace '
','
'
140 | $Fragment1 = $Fragment1 -replace '',''
141 | $Fragment1 = $Fragment1 -replace '',''
142 | $Fragment1 = $Fragment1 -replace '',''
143 |
144 | # Add Bootstrap and Bootstrap tables
145 | $Precontent='Issue Work Completed by Team'
146 | $Postcontent=''
147 |
148 | $ConvertedHTML = ConvertTo-HTML -Body "$Fragment1" -Head $Precontent -PostContent $Postcontent
149 |
150 | # Find and replace necessary elements in converted HTML that are outside of fragments
151 | $ConvertedHTML = $ConvertedHTML -replace '', ''
152 | $ConvertedHTML = $ConvertedHTML -replace '', ''
153 | $ConvertedHTML = $ConvertedHTML -replace ' | | | | | | | | | |
', '' # remove empty rows
154 |
155 | $ConvertedHTML |
156 | Out-File "C:\Temp\IssuesWorkCompleted.html"
--------------------------------------------------------------------------------
/Get-UpdatedAppForAzDevOpsBuild.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .Description
3 | Script to determine which app should be built.
4 | .Outputs
5 | "App*Updated" variable set that can be used in subsequent build task conditions.
6 | e.g. and(succeeded(), eq(variables['App1Updated'], 'True'))
7 | #>
8 |
9 | # Get all files that changed
10 | # https://git-scm.com/docs/git-diff
11 | $EditedFiles = git diff HEAD HEAD~ --name-only
12 |
13 | # Check each file that was changed and set variable
14 | $EditedFiles | ForEach-Object {
15 | Switch -Wildcard ($_ ) {
16 | "App1/*" {
17 | Write-Host "App 1 changed"
18 | Write-Host "##vso[task.setvariable variable=App1Updated]True"
19 | }
20 | "App2/*" {
21 | Write-Host "App 2 changed"
22 | Write-Host "##vso[task.setvariable variable=App2Updated]True"
23 | }
24 | "App3/*" {
25 | Write-Host "App 3 changed"
26 | Write-Host "##vso[task.setvariable variable=App3Updated]True"
27 | }
28 | # Add the rest of the App path filters here
29 | }
30 | }
--------------------------------------------------------------------------------
/IIS_Deploy.psm1:
--------------------------------------------------------------------------------
1 | #region IIS functions
2 |
3 | Function Install-DotNetCoreHostingBundle {
4 | <#
5 | .Synopsis
6 | Install dotnet core hosting bundle (if not already installed)
7 |
8 | .Description
9 | Checks if dotnet core hosting bundle is already installed. If it is, do nothing.
10 | If it is not, use dotnet-hosting-2.1.6-win.exe to install the bundle quietly.
11 |
12 | After the install, resets IIS to pick up changes made to system PATH en variable
13 |
14 | .Example
15 | Install-DotNetCoreHostingBundle
16 | #>
17 | $vm_dotnet_core_hosting_module = Get-WebGlobalModule | where-object { $_.name.ToLower() -eq "aspnetcoremodule" }
18 | If (!$vm_dotnet_core_hosting_module){
19 | ".Net core hosting module is not installed."
20 | "Starting install of dotnet-hosting-2.1.6..."
21 | .\dotnet-hosting-2.1.6-win.exe /install /quiet /norestart
22 |
23 | # Restarting IIS picks up a change to the system PATH, which is an environment variable, made by the installer.
24 | "Restarting IIS to pick up changes to system PATH..."
25 | cmd.exe /c "net stop was /y"
26 | cmd.exe /c "net start w3svc"
27 | }
28 | Else{
29 | ".Net core hosting bundle already installed."
30 | }
31 | }
32 |
33 | Function Install-IISURLRewrite {
34 | <#
35 | .Synopsis
36 | Install IIS URL Rewrite module (if not already installed)
37 |
38 | .Description
39 | Checks if IIS URL Rewrite module is already installed, if it is, do nothing.
40 | If it is not, use WebPlatformInstaller_amd64_en-US.msi to install the quietly.
41 |
42 | .Example
43 | Install-IISURLRewrite
44 | #>
45 | $vm_URL_Rewrite_module = Get-WebGlobalModule | where-object { $_.name.ToLower() -eq "RewriteModule" }
46 | If (!$vm_URL_Rewrite_module){
47 | "URL Rewrite module is not installed."
48 | "Starting install of urlrewrite2.exe..."
49 | Start-Process './WebPlatformInstaller_amd64_en-US.msi' '/qn' -PassThru | Wait-Process
50 | Set-Location 'C:/Program Files/Microsoft/Web Platform Installer'; .\WebpiCmd.exe /Install /Products:'UrlRewrite2' /AcceptEULA
51 | }
52 | Else{
53 | "URL Rewrite module already installed."
54 | }
55 | }
56 |
57 | Function New-AppPool {
58 | <#
59 | .Synopsis
60 | Creates new IIS Application Pool (if it does not exist)
61 |
62 | .Description
63 | Checks if app pool exists, if it does do nothing.
64 | If it does not, create it.
65 |
66 | .Example
67 | New-AppPool -iisAppPoolName "TestAppPoolName" -iisIdentity "DOMAIN\FakeUser"
68 | #>
69 | [CmdletBinding()]
70 | Param
71 | (
72 | [Parameter(Mandatory)]
73 | $iisAppPoolName,
74 | [Parameter(Mandatory)]
75 | $iisIdentity,
76 | [Parameter(Mandatory=$false)]
77 | $iisAppPoolDotNetVersion = "",
78 | [Parameter(Mandatory=$false)]
79 | $iisAppPoolManagedPipelineMode = ""
80 | )
81 |
82 | # WebAdministration Module Required
83 | Import-Module WebAdministration
84 |
85 | # Navigate to the app pools root
86 | Set-Location IIS:\AppPools
87 |
88 | # Check if the app pool exists
89 | If (!(Test-Path $iisAppPoolName -pathType container)) {
90 | # Create the app pool
91 | "App pool does not exist."
92 | "Creating $iisAppPoolName app pool..."
93 | $appPool = New-Item $iisAppPoolName
94 | $appPoolPath = "IIS:\AppPools\"+ $appPool.name
95 | "Resetting IIS to avoid locks on applicationHost.config"
96 | iisreset /restart
97 | Set-ItemProperty $appPoolPath -Name managedRuntimeVersion -Value $iisAppPoolDotNetVersion
98 | "Set managedRuntimeVersion"
99 | "Resetting IIS to avoid locks on applicationHost.config..."
100 | iisreset /restart
101 | Set-ItemProperty $appPoolPath -Name processModel -Value @{userName="$iisIdentity";password="$iisIdentityPassword";identitytype=3} -Force
102 | "Set app pool to run as: $iisIdentity "
103 |
104 | # Default Managed Pipeline Mode is Integrated.
105 | # If param $iisAppPoolManagedPipelineMode is set to Classic, set it.
106 | If($iisAppPoolManagedPipelineMode -eq "Classic"){
107 | "Setting Managed Pipeline Mode to Classic..."
108 | $iisAppPool = Get-Item IIS:\AppPools\$iisAppPoolName
109 | $iisAppPool.managedPipelineMode="Classic"
110 | $iisAppPool | set-item
111 | }
112 | "App pool created."
113 | }
114 | Else{
115 | "App pool already exists."
116 | }
117 | }
118 |
119 | Function New-WindowsAuthWebApp {
120 | <#
121 | .Synopsis
122 | Creates new IIS Application that uses Windows Auth (if it does not exist)
123 |
124 | .Description
125 | Checks if app exists, if it does, do nothing.
126 | If it does not, create it and enable Windows auth.
127 |
128 | .Example
129 | New-WindowsAuthWebApp -iisAppPoolName "TestAppPoolName" -iisAppName "TestAppName" -directoryPath "C:\Inetpub"
130 | #>
131 | [CmdletBinding()]
132 | Param
133 | (
134 | [Parameter(Mandatory)]
135 | $iisAppPoolName,
136 | [Parameter(Mandatory)]
137 | $iisAppName,
138 | [Parameter(Mandatory)]
139 | $directoryPath
140 | )
141 | # WebAdministration Module Required
142 | Import-Module WebAdministration
143 |
144 | # Navigate to the sites root
145 | Set-Location "IIS:\Sites\Default Web Site"
146 |
147 | # Check if the app exists
148 | If ( -Not (Get-WebApplication $iisAppName) ) {
149 | # Create the app
150 | "App does not exist."
151 | "Creating $iisAppName..."
152 | "Resetting IIS to avoid locks on applicationHost.config..."
153 | iisreset /restart
154 | New-WebApplication $iisAppName -ApplicationPool $iisAppPoolName -PhysicalPath $directoryPath -Force
155 | "App created."
156 | "Disabling anonymous authentication..."
157 | Set-WebConfigurationProperty -filter /system.webServer/security/authentication/anonymousAuthentication -name enabled -value false -PSPath IIS:\\ -location "Default Web Site/$iisAppName"
158 | "Enabling Windows Auth.."
159 | Set-WebConfigurationProperty -filter /system.webServer/security/authentication/windowsAuthentication -name enabled -value true -PSPath IIS:\\ -location "Default Web Site/$iisAppName"
160 | }
161 | Else{
162 | "App already exists."
163 | }
164 | }
165 |
166 | Function New-AnonAuthWebApp {
167 | <#
168 | .Synopsis
169 | Creates new IIS Application that uses Anonymous Auth (if it does not exist)
170 |
171 | .Description
172 | Checks if app exists, if it does, do nothing.
173 | If it does not, create it and enable Anonymous auth.
174 |
175 | .Example
176 | New-AnonAuthWebApp -iisAppPoolName "TestAppPoolName" -iisAppName "TestAppName" -directoryPath "C:\Inetpub\TestAppName"
177 |
178 | #>
179 | Param
180 | (
181 | [Parameter(Mandatory)]
182 | $iisAppPoolName,
183 | [Parameter(Mandatory)]
184 | $iisAppName,
185 | [Parameter(Mandatory)]
186 | $directoryPath
187 | )
188 | # WebAdministration Module Required
189 | Import-Module WebAdministration
190 |
191 | # Navigate to the sites root
192 | Set-Location "IIS:\Sites\Default Web Site"
193 |
194 | # Check if the app exists
195 | If ( -Not (Get-WebApplication $iisAppName) ) {
196 | # Create the app
197 | "App does not exist."
198 | "Creating $iisAppName..."
199 | "Resetting IIS to avoid locks on applicationHost.config..."
200 | iisreset /restart
201 | New-WebApplication $iisAppName -ApplicationPool $iisAppPoolName -PhysicalPath $directoryPath -Force
202 | "App created."
203 | "Enabling anonymous authentication..."
204 | Set-WebConfigurationProperty -filter /system.webServer/security/authentication/anonymousAuthentication -name enabled -value true -PSPath IIS:\\ -location "Default Web Site/$iisAppName"
205 | "Disabling Windows Auth.."
206 | Set-WebConfigurationProperty -filter /system.webServer/security/authentication/windowsAuthentication -name enabled -value false -PSPath IIS:\\ -location "Default Web Site/$iisAppName"
207 | }
208 | Else{
209 | "App already exists."
210 | }
211 | }
212 |
213 | Function New-Website {
214 | <#
215 | .Synopsis
216 | Creates new IIS Website that uses Anonymous Auth (if it does not exist)
217 |
218 | .Description
219 | Checks if website exists, if it does, do nothing.
220 | If it does not, create it and enable Anonymous auth.
221 |
222 | .Example
223 | New-Website -iisAppPoolName "TestAppPoolName" -iisAppName "TestAppName" -directoryPath "C:\Inetpub\TestAppName" -iisPort "80" -iisPortSSL "443"
224 |
225 | #>
226 | [CmdletBinding()]
227 | Param
228 | (
229 | [Parameter(Mandatory)]
230 | $iisAppPoolName,
231 | [Parameter(Mandatory)]
232 | $iisAppName,
233 | [Parameter(Mandatory)]
234 | $directoryPath,
235 | [Parameter(Mandatory)]
236 | $iisPort,
237 | [Parameter(Mandatory)]
238 | $iisPortSSL
239 | )
240 | # WebAdministration Module Required
241 | Import-Module WebAdministration
242 |
243 | # Navigate to the sites root
244 | Set-Location "IIS:\Sites"
245 |
246 | # Check if the app exists
247 | If ( -Not (Get-Website $iisAppName) ) {
248 | # Create the app
249 | "Website does not exist."
250 | "Creating $iisAppName..."
251 | "Resetting IIS to avoid locks on applicationHost.config..."
252 | iisreset /restart
253 |
254 | # Create Website
255 | New-Website -Name $iisAppName -ApplicationPool $iisAppPoolName -PhysicalPath $directoryPath
256 |
257 | # Add http and https bindings if port for https is passed
258 | If($iisPortSSL -ne "False"){
259 | "Getting IP address for binding..."
260 | $iisIPAddress=((ipconfig | findstr [0-9].\.)[0]).Split()[-1]
261 | "IP is: $iisIPAddress"
262 | "Adding https binding..."
263 | New-WebBinding -Name $iisAppName -Protocol https -Port $iisPortSSL -IPAddress $iisIPAddress -SslFlags 0
264 | "Adding cert to https binding..."
265 | $CertCN = "$env:ComputerName"
266 | $Thumbprint = (Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {$_.Subject -like "*$CertCN*"}).Thumbprint;
267 | $httpsBinding = Get-WebBinding -Name $iisAppName -Protocol "https"
268 | $httpsBinding.AddSslCertificate($Thumbprint, "my")
269 | "Removing default http binding that's added when creating site..."
270 | Get-WebBinding -Name $iisAppName -Protocol http | Remove-WebBinding
271 | "Adding new http binding..."
272 | New-WebBinding -Name $iisAppName -Protocol http -Port $iisPort -IPAddress $iisIPAddress -SslFlags 0
273 | }
274 | Else{
275 | "Adding http binding..."
276 | New-WebBinding -Name $iisAppName -Protocol http -Port $iisPort -IPAddress $iisIPAddress -SslFlags 0
277 | }
278 | "App created."
279 |
280 | "Disabling anonymous authentication..."
281 | Set-WebConfigurationProperty -filter /system.webServer/security/authentication/anonymousAuthentication -name enabled -value false -PSPath IIS:\\ -location "$iisAppName"
282 | "Enabling Windows Auth.."
283 | Set-WebConfigurationProperty -filter /system.webServer/security/authentication/windowsAuthentication -name enabled -value true -PSPath IIS:\\ -location "$iisAppName"
284 | }
285 | Else{
286 | "Website already exists."
287 | }
288 | }
289 | #endregion IIS functions
--------------------------------------------------------------------------------
/IIS_SetPropertiesForFasterStartup.ps1:
--------------------------------------------------------------------------------
1 | #Sets IIS properties for faster load times
2 |
3 | #Load IIS Module if not loaded already
4 | Function WebAdministration-VerifyModule
5 | {
6 | If ( ! (Get-module WebAdministration ))
7 | {
8 | Try
9 | {
10 | Import-Module WebAdministration
11 | }
12 | Catch
13 | {
14 | Write-Host "Exception Type: $($_.Exception.GetType().FullName)"
15 | Write-Host "Exception Message: $($_.Exception.Message)"
16 | }
17 | }
18 | $message = "The module WebAdministration verified at (" + (Get-Date).ToString('yyyy.MM[MMM].dd HH:mm:ss zzz') + ")."
19 | Write-Host $message
20 | return $true
21 | }
22 |
23 | Function SetIISProperties-AppPools
24 | {
25 | Try
26 | {
27 | #Loop through each app pool
28 | dir IIS:\Sites | ForEach-Object { $appPool = $_.applicationPool
29 | #set start mode to always running
30 | Set-ItemProperty IIS:\AppPools\$appPool -name startMode -value "alwaysrunning"
31 | #set idle timeout to 0 seconds
32 | Set-ItemProperty IIS:\AppPools\$appPool -name processModel.idleTimeout -value "0"
33 | write-output "$appPool app pool set to startMode:always running, idle timeout: 0 seconds"
34 | }
35 | }
36 | Catch
37 | {
38 | Write-Host "Exception Type: $($_.Exception.GetType().FullName)"
39 | Write-Host "Exception Message: $($_.Exception.Message)"
40 | }
41 | }
42 |
43 | Function SetIISProperties-Sites
44 | {
45 | Try
46 | {
47 | #Loop through each site
48 | dir IIS:\Sites | ForEach-Object { $siteName = $_.Name
49 | #set preLoadEnabled to true
50 | Set-WebConfigurationProperty "`/system.applicationHost`/sites`/site[@name=`"$siteName`"]/application" -Name "preloadEnabled" -Value "true" -PSPath IIS:\
51 | write-output "$siteName site set to preloadEnabled:True"
52 | }
53 | }
54 | Catch
55 | {
56 | Write-Host "Exception Type: $($_.Exception.GetType().FullName)"
57 | Write-Host "Exception Message: $($_.Exception.Message)"
58 | }
59 | }
60 |
61 | #Verify module then run functions
62 | Try
63 | {
64 | If (WebAdministration-VerifyModule)
65 | {
66 | SetIISProperties-AppPools
67 | SetIISProperties-Sites
68 | }
69 | Else
70 | {
71 | Write-Error "Module: WebAdministration did not load!"
72 | }
73 | }
74 | Catch
75 | {
76 | Write-Error "Something wrong with Module: WebAdministration"
77 | }
78 |
79 | Exit 0
80 |
--------------------------------------------------------------------------------
/JBoss_FTPUploadLogs.ps1:
--------------------------------------------------------------------------------
1 | # Uploads desired logs to FTP site
2 | # Requires PowerShell V 3.0, 7-Zip
3 |
4 | #UI Settings
5 | $a = (Get-Host).UI.RawUI
6 | $b = $a.WindowSize
7 | $b.Width = 60
8 | $b.Height = 30
9 | $a.WindowSize = $b
10 |
11 | [int]$serverNumber = read-host "Upload logs to FTP`n1-Grp-Test`n2-iGrp-Test`n3-Grp-Stage`n4-iGrp-Stage`n5-Grp-Production`n6-iGrp-Production`nSelect number"
12 |
13 | switch($serverNumber){
14 |
15 | #Grp-Test
16 | 1 {$Dir0 = "\\$machineName\$drive\$JBossDir\server\$node\log\*"
17 | $Dir1 = "\\$machineName\$drive\$JBossDir\server\$node\log\*"
18 | $Dir2 = "\\$machineName\$drive\logs\*"
19 | $Environment = "Test"
20 | $indOrGroup = "Grp"}
21 | #Ind-Test
22 | 2 {$Dir0 = "\\$machineName\$drive\$JBossDir\server\$node\log\*"
23 | $Dir1 = "\\$machineName\$drive\$JBossDir\server\$node\log\*"
24 | $Dir2 = "\\$machineName\$drive\logs\*"
25 | $Environment = "Test"
26 | $indOrGroup = "Ind"}
27 | #Note-Express logs from stage/prod include both environments
28 | #Grp-Stage
29 | 3 {$Dir0 = "\\$machineName\$drive\$JBossDir\server\$node\log\*"
30 | $Dir1 = "\\$machineName\$drive\$JBossDir\server\$node\log\*"
31 | $Dir2 = "\\$machineName\$drive\logs\*"
32 | $Dir3 = "\\$machineName\$drive\logs\*"
33 | $Environment = "Stage"
34 | $indOrGroup = "Grp"}
35 | #Ind-Stage
36 | 4 {$Dir0 = "\\$machineName\$drive\$JBossDir\server\$node\log\*"
37 | $Dir1 = "\\$machineName\$drive\$JBossDir\server\$node\log\*"
38 | $Dir2 = "\\$machineName\$drive\logs\*"
39 | $Dir3 = "\\$machineName\$drive\logs\*"
40 | $Environment = "Stage"
41 | $indOrGroup = "Ind"}
42 | #Grp-Production
43 | 5 {$Dir0 = "\\$machineName\$drive\$JBossDir\server\$node\log\*"
44 | $Dir1 = "\\$machineName\$drive\$JBossDir\server\$node\log\*"
45 | $Dir2 = "\\$machineName\$drive\$JBossDir\server\$node\log\*"
46 | $Dir3 = "\\$machineName\$drive\$JBossDir\server\$node\log\*"
47 | $Dir4 = "\\$machineName\$drive\logs\*"
48 | $Dir5 = "\\$machineName\$drive\logs\*"
49 | $Environment = "Production"
50 | $indOrGroup = "Grp"}
51 | #Ind Production
52 | 6 {$Dir0 = "\\$machineName\$drive\$JBossDir\server\$node\log\*"
53 | $Dir1 = "\\$machineName\$drive\$JBossDir\server\$node\log\*"
54 | $Dir2 = "\\$machineName\$drive\$JBossDir\server\$node\log\*"
55 | $Dir3 = "\\$machineName\$drive\$JBossDir\server\$node\log\*"
56 | $Dir4 = "\\$machineName\$drive\logs\*"
57 | $Dir5 = "\\$machineName\$drive\logs\*"
58 | $Environment = "Production"
59 | $indOrGroup = "Ind"}
60 | default {"Directory could not be determined."}
61 | }
62 |
63 | # Copy log files to network drive and zip before upload.
64 | # Delete and recreate folders so previously uploaded files are not included
65 | Remove-Item \\example\Logs\Dir* -Recurse -Force
66 | New-Item -ItemType Directory\example\Logs\Dir0
67 | New-Item -ItemType Directory\example\Logs\Dir1
68 | New-Item -ItemType Directory\example\Logs\Dir2
69 | if($Environment -eq "Production" -OR $Environment -eq "Stage"){
70 | New-Item -ItemType Directory\example\Logs\Dir3}
71 | if($Environment -eq "Production"){
72 | New-Item -ItemType Directory\example\Logs\Dir4
73 | New-Item -ItemType Directory\example\Logs\Dir5}
74 |
75 | # Mulitple folders required because copy-item forced to overwrite files (no dupes allowed)
76 | Copy-Item $Dir0 \\example\Logs\Dir0 -Force -Recurse
77 | Copy-Item $Dir1 \\example\Logs\Dir1 -Force -Recurse
78 | Copy-Item $Dir2 \\example\Logs\Dir2 -Force -Recurse
79 |
80 | # Staging has 2 JBoss nodes 2 express/ Production has 4 JBoss Nodes and 2 express
81 | If($Environment -eq "Production" -or $Environment -eq "Stage"){
82 | Copy-Item $Dir3 \\example\Logs\Dir3 -Force -Recurse
83 | }
84 | If($Environment -eq "Production"){
85 | Copy-Item $Dir4 \\example\Logs\Dir4 -Force -Recurse
86 | Copy-Item $Dir5 \\example\Logs\Dir5 -Force -Recurse
87 | }
88 |
89 | # 7-Zip required to zip logs, make sure it's installed
90 | if(-not (test-path "$env:ProgramFiles\7-Zip\7z.exe"))
91 | {
92 | throw "$env:ProgramFiles\7-Zip\7z.exe needed"
93 | }
94 | # Set Alias for 7-zip to sz
95 | set-alias sz "$env:ProgramFiles\7-Zip\7z.exe"
96 |
97 | # Directory of logs to zip
98 | $filePath = "\\example\Repository\logs"
99 | $logs = Get-ChildItem -Recurse -Path $filePath |
100 | Where-Object { $_.Extension -eq ".log" }
101 |
102 | # Place new logs.zip in \example\Logs\
103 | foreach ($file in $logs)
104 | {
105 | sz a -t7z "\example\Repository\Logs" "\example\Logs"
106 | }
107 |
108 | # ftp server info
109 | $ftp = ""
110 | $user = ""
111 | $pass = ""
112 |
113 | $webclient = New-Object System.Net.WebClient
114 | $webclient.Credentials = New-Object System.Net.NetworkCredential($user,$pass)
115 |
116 | # Uploads logs.zip to FTP
117 | foreach($item in (dir "\example\" "*.7z")){
118 | "Uploading $item..."
119 | $uri = New-Object System.Uri($ftp+$item.Name)
120 | $webclient.UploadFile($uri, $item.FullName)
121 | }
122 |
123 | [int]$sendEmail = Read-Host "Send email to Bob? (1) Yes (2) No"
124 |
125 | if($sendEmail -eq 1)
126 | {
127 | Send-MailMessage -To "Bob@Bob.com" -SmtpServer mail.bob.com -Subject "$indOrGroup - $Environment logs have been uploaded to FTP"
128 | }
129 |
--------------------------------------------------------------------------------
/O365_UndeliverableMessageExport.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .Synopsis
3 | X Mailbox Undeliverable message export and filter
4 | .DESCRIPTION
5 | This script uses ExportOSCEXOEmailMessage PowerShell module (in addition to the Exchange Web API) to connect to the O365 X mailbox,
6 | search for emails according to X criteria during the last week, exports them,
7 | filters the emails for email addresses and saves them to undeliverableEmailList.txt, then sends email with attachment
8 | .OUTPUTS
9 | undeliverableEmailList.txt
10 | .NOTES
11 | Prerequisites:
12 | Exchange Web API https://www.microsoft.com/en-us/download/details.aspx?id=35371
13 | Export-OSCEXOEmailMessage module https://gallery.technet.microsoft.com/office/Export-Email-Messages-from-1419bbe9
14 | #>
15 |
16 | Import-Module "ExportOSCEXOEmailMessage.psm1"
17 |
18 | # Set directory to store emails and save output
19 | $emailDir = "C:\temp\emails"
20 |
21 | # Start WinRM service for remoting (not running by default)
22 | Get-Service "Winrm" | Start-Service
23 |
24 | # Connect to O365
25 | # Create secure string for credentials so prompt for get-credential isn't required
26 | $username = "x@x.com"
27 | $password = ''
28 | $secstr = New-Object -TypeName System.Security.SecureString
29 | $password.ToCharArray() | ForEach-Object {$secstr.AppendChar($_)}
30 | $cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $secstr
31 | $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell/ -Credential $cred -Authentication Basic -AllowRedirection
32 | Import-PSSession $Session
33 |
34 | # Connect to OSCEXO
35 | Connect-OSCEXOWebService -Credential $cred
36 |
37 | # Create a new search folder using start and end dates; range of last 5 days.
38 | $EndDate = Get-Date -format G
39 | $StartDateNoFormat = (Get-Date).AddDays(-5)
40 | $StartDate = Get-Date $StartDateNoFormat -Format G
41 |
42 | # New search folder includes inbox messages
43 | New-OSCEXOSearchFolder -DisplayName "X.Inbox $EndDate" -Traversal Deep -WellKnownFolderName Inbox -StartDate $StartDate -EndDate $EndDate
44 | Start-Sleep -Seconds 5 # Sleep required: export fails before the search folder creation is complete
45 | # Export search folder results
46 | Get-OSCEXOSearchFolder "Operations.Response.Inbox $EndDate" |
47 | Export-OSCEXOEmailMessage -Path $emailDir -KeepSearchFolder
48 |
49 | # Copy emails with body content matches to valid email folder
50 | $bodyContentMatches = "example"
51 | (Get-ChildItem "$emailDir\*eml" |
52 | Select-String -SimpleMatch -Pattern $bodyContentMatches |
53 | Select-Object -ExpandProperty path -Unique) |
54 | ForEach-Object{Copy-Item -LiteralPath $_ $emailDir\validEmails}
55 |
56 | # Search for all email addresses using regex
57 | $input_path = "$emailDir\validEmails\*eml"
58 | $regex = ‘(\b[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}\b)|(^\..*)’ # Filter valid emails, include lines starting with periods
59 | select-string -SimpleMatch -Path $input_path -Pattern "MsoNormal" -context 1,3 |
60 | ForEach {$_ -Replace "@", ""}
61 |
62 | select-string -Path $input_path -Pattern $regex -AllMatches |
63 | # Exclude X addresses, postmaster, addresses with more than 8 digits, misc.
64 | Where-Object {$_ -NotLike "*X*" -and $_ -NotLike "*X*" |
65 | % { $_.Matches } |
66 | % { $_.Value } > $emailDir\validEmails\undeliverableEmailListDupes.txt
67 |
68 | # Remove duplicates
69 | $date = Get-Date -Format D # File timestamp for output
70 | Get-Content $emailDir\validEmails\undeliverableEmailListDupes.txt |
71 | Where-Object {$_ -notmatch "(.*\d.*){6}" -and $_ -notmatch '^[\.].*'} | # Remove lines starting with periods
72 | sort |
73 | select -unique > "$emailDir\validEmails\undeliverableEmailList_$date.txt"
74 |
75 | # Send email to x with attachment
76 | $un = "domain\x@x.com"
77 | $pw = ''
78 | $secstring = New-Object -TypeName System.Security.SecureString
79 | $pw.ToCharArray() | ForEach-Object {$secstring.AppendChar($_)}
80 | $credential = new-object -typename System.Management.Automation.PSCredential -argumentlist $un, $secstring
81 | Send-MailMessage -To X@X.com -cc X@X.com -Subject "Undeliverable Email List $StartDate - $EndDate" -Attachments "$emailDir\validEmails\undeliverableEmailList_$date.txt" -From "X@X.com" -SmtpServer smtp.office365.com -Port 587 -Credential $credential -UseSsl
82 |
83 | # Cleanup
84 | Get-Item $emailDir\*.eml | Remove-Item
85 | Get-Item $emailDir\validEmails\*.eml | Remove-Item
86 | Get-Item $emailDir\validEmails\undeliverableEmailListDupes.txt | Remove-Item
87 |
--------------------------------------------------------------------------------
/Octopus_UpdateVariable.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .DESCRIPTION
3 | Script to update Octopus variable via Octopus API.
4 | Can be run manually or automated through an Orchestration tool/build system
5 | such as TFS/VSTS, Jenkins, TeamCity, etc.
6 |
7 | .EXAMPLE
8 | Example build step using TFS/VSTS to update BuildID Octopus variable with build ID system variable value
9 | 1. Add VSTS/TFS PowerShell script build step
10 | 2. Add APIKey as secret build definition variable
11 | 3. Pass as arguments in build step: BuildID $(Build.BuildID) $(APIKey) OctopusProjectName "http://MyOctopusInstance/octopus/"
12 | #>
13 |
14 | Param
15 | (
16 | # Variable name to update
17 | [string(Mandatory=$true)]$VarName,
18 |
19 | # New value for variable
20 | [string(Mandatory=$true)]$Newvalue,
21 |
22 | # Octopus APIKey (should be encrypted build variable)
23 | [string(Mandatory=$true)]$APIKey,
24 |
25 | # Octopus Project Name
26 | [string(Mandatory=$true)]$ProjectName,
27 |
28 | # URL to your Octopus server instance
29 | [string(Mandatory=$true)]$OctopusURL
30 | )
31 |
32 | Function UpdateOctopusVariable
33 | {
34 | Begin
35 | {
36 | <#
37 | Load required assemblies. They can be found in the Octopus Tentacle MSI: https://octopus.com/downloads
38 | These should be copied to your build server(s) if running via TFS/VSTS build.
39 | #>
40 | Add-Type -Path "$env:PROGRAMFILES\Octopus\Newtonsoft.Json.dll"
41 | Add-Type -Path "$env:PROGRAMFILES\Octopus\Octopus.Client.dll"
42 |
43 | # Connection data
44 | $endpoint = new-object Octopus.Client.OctopusServerEndpoint ($OctopusURL, $APIKey)
45 | $repository = new-object Octopus.Client.OctopusRepository $endpoint
46 | }
47 |
48 | Process
49 | {
50 | # Get project
51 | $project = $repository.Projects.FindByName($ProjectName)
52 |
53 | # Get project's variable set
54 | $variableset = $repository.VariableSets.Get($project.links.variables)
55 |
56 | # Get variable to update
57 | $variable = $variableset.Variables | ?{$_.name -eq $VarName}
58 |
59 | # Update variable
60 | $variable.Value = $newvalue
61 | }
62 |
63 | End
64 | {
65 | # Save variable set
66 | $repository.VariableSets.Modify($variableset)
67 | }
68 | }
69 | Try
70 | {
71 | write-host "Begin updating variable: $VarName value:$Newvalue "
72 | UpdateOctopusVariable
73 | write-host "Done updating variable: $VarName value:$Newvalue "
74 |
75 | }
76 | Catch
77 | {
78 | $result += $actionFailed
79 | $result += "`r`n Exception Type: $($_.Exception.GetType().FullName)"
80 | $result += "`r`n Exception Message: $($_.Exception.Message)"
81 | Exit 1
82 | }
83 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PowerShell
2 |
3 | Misc scripts to interact with TFS/Azure DevOps REST API, AWS, deploy SSIS projects, set IIS properties, upload logs to FTP site, etc.
4 |
--------------------------------------------------------------------------------
/SSIS_DeployISPAC.ps1:
--------------------------------------------------------------------------------
1 | # Script to deploy SSIS package (ISPAC)
2 | # ISPAC is built using devenv.exe
3 | # Use computer name to get IP then store in $server variable for connection string below
4 | $server = $env:computername
5 | $ips = [System.Net.Dns]::GetHostAddresses($server)[0].IPAddressToString;
6 | $server = $ips
7 | write-host "Server IP:" $server
8 |
9 | # Load the IntegrationServices Assembly
10 | $loadStatus = [Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Management.IntegrationServices,Culture=neutral")
11 |
12 | # Create a connection to the server
13 | $constr = "Data Source=$server;Initial Catalog=master;Integrated Security=SSPI;"
14 | $con = New-Object System.Data.SqlClient.SqlConnection $constr
15 |
16 | # Store the IntegrationServices Assembly namespace to avoid typing it every time
17 | $ISNamespace = "Microsoft.SqlServer.Management.IntegrationServices"
18 | Write-Host "Connecting to server ..."
19 |
20 | # Create the Integration Services object
21 | $ssis = New-Object $ISNamespace".IntegrationServices" $con
22 |
23 | # Check if catalog exists
24 | if ($ssis.Catalogs.Count -eq 0)
25 | {
26 | Write-Error "SSISDB doesn't exist"
27 | throw "SSISDB doesn't exist"
28 | }
29 |
30 | # Set catalog to SSISDB
31 | $cat = $ssis.Catalogs["SSISDB"]
32 |
33 | # If $ProjectName folder in SSISDB doesn't exist, create it
34 | $folderName = "$ProjectName"
35 | if ($cat.Folders[$folderName] -eq $null)
36 | {
37 | Write-Host "Creating new folder" $folderName
38 | $newfolder = New-Object $ISNamespace".CatalogFolder" ($cat, $folderName, "Description")
39 | $newfolder.Create()
40 | }
41 |
42 | # Set folder to catalog folder ($ProjectName)
43 | $folder = $cat.Folders[$folderName]
44 |
45 | # Set dir of ISPAC file
46 | $localToLocalETLFullPath = "$PSScriptRoot\bin\$ProjectName.$DatabaseName.ispac"
47 |
48 | # Read the project file, and deploy it to the folder
49 | Write-Host "Deploying SSIS project ..."
50 | [byte[]] $projectFile = [System.IO.File]::ReadAllBytes($localToLocalETLFullPath)
51 | $folder.DeployProject("$ProjectName.$DatabaseName.ETL", $projectFile)
52 |
--------------------------------------------------------------------------------
/TFS_AggregateCodeCoverageResults.ps1:
--------------------------------------------------------------------------------
1 | # Script to aggregate TFS 2015 build code coverage results for specified build definition
2 | # SSRS reports do not work with new build system (Non XAML) to provide this info as of Update 2
3 |
4 | # Encrypted password passed via build definition variable
5 | param(
6 | [string]$passwd
7 | )
8 |
9 | Function AggregateCodeCoverageResults
10 | {
11 | $definitionId = ""
12 | $user = ""
13 | $secpasswd = ConvertTo-SecureString $passwd -AsPlainText -Force
14 | $credential = New-Object System.Management.Automation.PSCredential($user, $secpasswd)
15 |
16 | # Use build api to get build IDs and build dates.
17 | [uri] $buildUri = $env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI + $env:SYSTEM_TEAMPROJECT + "/_apis/build/builds?definitions=" + $definitionId +"&statusFilter=completed&reasonfilter=scheduled&api-version=2.0"
18 | $getBuilds = Invoke-RestMethod -Uri $buildUri -Method Get -Credential $credential
19 |
20 | # Create object from results containing build date and build number
21 | $buildList = $getBuilds |
22 | select @{Name="BuildDate"; Expression={$_.value.finishtime}},@{Name="BuildNumber"; Expression={$_.value.buildNumber}}
23 |
24 | # Build number will be used in code coverage Uri
25 | $buildNumbers = $buildList.BuildNumber
26 |
27 | # Build dates will be added to results array at line 70
28 | $buildDates = $buildList.BuildDate
29 |
30 | # Trim anything past xxxx-xx-xx
31 | $buildDatesArray = $buildDates.Substring(0,10)
32 |
33 | # Initialize array that will contain results
34 | $coverageResultsArray = @()
35 |
36 | # Loop through every build ID to get code coverage results
37 | ForEach($buildNumber in $buildNumbers)
38 | {
39 | # Use code coverege api to get results of build
40 | [uri] $codeCoverageUri = $env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI + $env:SYSTEM_TEAMPROJECT + "/_apis/test/codecoverage?api-version=2.0-preview.1&buildid=" + $buildNumber +"&flags=7"
41 | $coverageResults = Invoke-RestMethod -Uri $codeCoverageUri -Method Get -Credential $credential
42 |
43 | # Get total blocks covered and blocks NOT covered, sum them up to get toal blocks, then divide blocks covered by total blocks
44 | $blocksCovered = $coverageResults.value.modules.statistics.blocksCovered
45 | $blocksNotCovered = $coverageResults.value.modules.statistics.blocksNotCovered
46 | $blocksCoveredSum = $blockscovered | measure-object -sum
47 | $blocksNotCoveredSum = $blocksNotCovered | measure-object -sum
48 | $totalBlocks = $blocksCoveredSum.sum+$blocksNotCoveredSum.sum
49 | $dividedBlocks = $blocksCoveredSum.sum/$totalBlocks
50 |
51 | # move decimal over 2, add percent symbol
52 | $coveragePercent = "{00:P2}" -f $dividedBlocks
53 |
54 | # Failed builds return NaN for coverage result
55 | if($coveragePercent -eq "NaN")
56 | {
57 | # Add "build failed" to results array
58 | $coverageResultsArray += "Build Failed"
59 | }
60 | else
61 | {
62 | # Add coverage percent to results array
63 | $coverageResultsArray += $coveragePercent
64 | }
65 |
66 | }
67 |
68 | # Loop through and add build dates to coverage results output to CSV
69 | $(for ($i=0; $i -le $coverageResultsArray.Count;$i++)
70 | {
71 | 'Build Date: {0} Coverage: {1}' -f $buildDatesArray[$i],$coverageResultsArray[$i]
72 | }) | set-content C:\codecoverage.csv
73 |
74 | }
75 | Try
76 | {
77 | AggregateCodeCoverageResults
78 | }
79 | Catch
80 | {
81 | Write-Host "Exception Type: $($_.Exception.GetType().FullName)"
82 | Write-Host "Exception Message: $($_.Exception.Message)"
83 | }
84 |
--------------------------------------------------------------------------------
/TFS_CreateBranchPolicies.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Uses the TFS REST API to apply branch policies to specified branch
4 | .DESCRIPTION
5 | This script uses the TFS REST API for configurations to set branch policies for:
6 | -approver group
7 | -Minimum approvers (2)
8 | -Required build
9 | -Work Item required
10 | .NOTES
11 | -Branch is set via build definition variable at queue time
12 | #>
13 | param(
14 | [string]$global:branch, # Branch to set policies on passed via build definition when queueing build
15 | [string]$global:user, # Service account user name passed via build definition
16 | [string]$global:passwd # Encrypted password passed via build definition
17 | )
18 |
19 | # Use TFS REST API for configurations: https://www.visualstudio.com/en-us/docs/integrate/api/policy/configurations#create-a-policy-configuration
20 | [uri] $global:PolicyUri = $env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI + $env:SYSTEM_TEAMPROJECT + "/_apis/policy/configurations?api-version=2.0-preview"
21 | write-host $PolicyUri
22 |
23 | # Create secure credential
24 | $global:secpasswd = ConvertTo-SecureString $passwd -AsPlainText -Force
25 | $global:credential = New-Object System.Management.Automation.PSCredential($user, $secpasswd)
26 |
27 | Function ApplyApproverPolicy
28 | {
29 |
30 | # JSON for setting appovers
31 | $JSONBody= @"
32 | {
33 | "isEnabled": true,
34 | "isBlocking": true,
35 | "type": {
36 | "id": ""
37 | },
38 | "settings": {
39 | "requiredReviewerIds": [
40 | ""
41 | ],
42 | "filenamePatterns": [
43 | "/ExamplePath"
44 | ],
45 | "addedFilesOnly": false,
46 | "scope": [
47 | {
48 | "repositoryId": null,
49 | "refName": "refs/heads/$Branch",
50 | "matchKind": "exact"
51 | },
52 | {
53 | "repositoryId": null,
54 | "refName": "refs/heads/$Branch/",
55 | "matchKind": "prefix"
56 | }
57 | ]
58 | }
59 | }
60 | "@
61 |
62 | # Use URI and JSON above to apply approver policy to specified branch
63 | Invoke-RestMethod -Uri $PolicyUri -Method Post -ContentType application/json -Body $JSONBody -Credential $credential
64 |
65 | }
66 |
67 | Function ApplyMinimumApproverPolicy
68 | {
69 |
70 | # JSON for setting minimum approval count policy
71 | $JSONBody= @"
72 | {
73 | "isEnabled": true,
74 | "isBlocking": true,
75 | "type": {
76 | "id": ""
77 | },
78 | "settings": {
79 | "minimumApproverCount": 2,
80 | "scope": [
81 | {
82 | "repositoryId": null,
83 | "refName": "refs/heads/$branch",
84 | "matchKind": "exact"
85 | }
86 | ]
87 | }
88 | }
89 | "@
90 |
91 | # Use URI and JSON above to apply minimum approver policy to specified branch
92 | Invoke-RestMethod -Uri $PolicyUri -Method Post -ContentType application/json -Body $JSONBody -Credential $credential
93 |
94 | }
95 |
96 | Function ApplyBuildPolicy
97 | {
98 |
99 | # JSON for setting required build policy
100 | $JSONBody= @"
101 | {
102 | "isEnabled": true,
103 | "isBlocking": true,
104 | "type": {
105 | "id": ""
106 | },
107 | "settings": {
108 | "buildDefinitionId": buildID,
109 | "scope": [
110 | {
111 | "repositoryId": null,
112 | "refName": "refs/heads/$branch",
113 | "matchKind": "exact"
114 | }
115 | ]
116 | }
117 | }
118 | "@
119 |
120 | # Use URI and JSON above to apply build policy to specified branch
121 | Invoke-RestMethod -Uri $PolicyUri -Method Post -ContentType application/json -Body $JSONBody -Credential $credential
122 |
123 | }
124 |
125 | Function ApplyWorkItemPolicy
126 | {
127 |
128 | # JSON for setting work item required policy
129 | $JSONBody= @"
130 | {
131 | "isEnabled": true,
132 | "isBlocking": true,
133 | "type": {
134 | "id": ""
135 | },
136 | "settings": {
137 | "scope": [
138 | {
139 | "repositoryId": null,
140 | "refName": "refs/heads/$branch",
141 | "matchKind": "exact"
142 | }
143 | ]
144 | }
145 | }
146 | "@
147 |
148 | # Use URI and JSON above to apply work item required to specified branch
149 | Invoke-RestMethod -Uri $PolicyUri -Method Post -ContentType application/json -Body $JSONBody -Credential $credential
150 |
151 | }
152 |
153 | Try
154 | {
155 | ApplyApproverPolicy
156 | write-host "Approver Policy set on branch: $branch"
157 | ApplyMinimumApproverPolicy
158 | write-host "Minimum Approver Policy set on branch: $branch"
159 | ApplyBuildPolicy
160 | write-host "Build Policy set on branch: $branch"
161 | ApplyWorkItemPolicy
162 | write-host "Work Item required Policy set on branch: $branch"
163 |
164 | }
165 | Catch
166 | {
167 | $result = $_.Exception.Response.GetResponseStream()
168 | $reader = New-Object System.IO.StreamReader($result)
169 | $reader.BaseStream.Position = 0
170 | $reader.DiscardBufferedData()
171 | $responseBody = $reader.ReadToEnd();
172 | write-host $responseBody
173 | }
174 | Finally
175 | {
176 | # Clear global variable to cleanup
177 | Clear-Variable user -Scope Global
178 | Clear-Variable pwd -Scope Global
179 | Clear-Variable branch -Scope Global
180 | Clear-Variable PolicyUri -Scope Global
181 | }
182 |
--------------------------------------------------------------------------------
/TFS_GetCommitAssociatedToBuild.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .Synopsis
3 | Script gets commit from last successful build.
4 | .Notes
5 | TFS REST API documentation: https://www.visualstudio.com/integrate/api/build/builds
6 | #>
7 |
8 | function GetCommitAssociatedToBuild{
9 | Param(
10 | [string]$DefinitionID = # Build Definition ID (found on variables tab)
11 | )
12 |
13 | # Get last completed build
14 | [uri] $BuildUri = $env:System_TeamFoundationCollectionUri + $env:System_TeamProject + "/_apis/build/builds?definitions=$DefinitionID&api-version=2.0&statusFilter=completed&`$top=1"
15 | $BuildInfo = Invoke-RestMethod -Method Get -Uri $BuildUri -ContentType 'application/json' -UseDefaultCredentials
16 |
17 | # Fail script if no build is found
18 | If($BuildInfo.count -ne 1){
19 | write-output "No build found"
20 | exit -1
21 | }
22 | # Get source version from build info
23 | $SourceVersion = $BuildInfo.value.sourceversion
24 | write-host "SourceVersion = $SourceVersion"
25 |
26 | # Write variable to be used in subsequent build steps
27 | Write-Host ("##vso[task.setvariable variable=SourceVersion;]$SourceVersion")
28 |
29 | }
30 |
31 | Try{
32 | GetCommitAssociatedToBuild
33 | }
34 | Catch{
35 | Write-Host "Exception Type: $($_.Exception.GetType().FullName)"
36 | Write-Host "Exception Message: $($_.Exception.Message)"
37 | exit 1
38 | }
39 |
--------------------------------------------------------------------------------
/TFS_PauseBuildsPerEnvironment.ps1:
--------------------------------------------------------------------------------
1 |
2 | <#
3 | .DESCRIPTION
4 | Gets all build definitions for specified environment name,
5 | enables or disables the CI triggers in order to prevent builds from
6 | being queued during demo's.
7 |
8 | .PARAMETER Action
9 | Enable or Disable build definition CI triggers
10 |
11 | .PARAMETER EnvironmentName
12 | Name of environment to disable/enable builds for
13 |
14 | .PARAMETER PAT
15 | Personal Access token. It's recommended to use a service account and pass via encrypted build definition variable.
16 |
17 | .Notes
18 | -These parameters are outside of functions in order to be passed by TFS build definition variables
19 | -Triggers aren't actually "disabled", just flipped Include/Exclude on path filter
20 | #>
21 | [CmdletBinding()]
22 | Param(
23 | [Parameter(Position=0,Mandatory)]
24 | [ValidateSet("Enable","Disable")]
25 | [string]$script:Action,
26 | [Parameter(Position=1,Mandatory)]
27 | [ValidateSet("Environment1","Environment2","Environment3")] # Case is ignored by default
28 | [string]$script:EnvironmentName,
29 | [Parameter(Position=2,Mandatory)]
30 | [ValidateNotNullOrEmpty()]
31 | [string]$script:PAT
32 | )
33 |
34 | # Base64-encodes the Personal Access Token (PAT) appropriately
35 | # This is required to pass PAT through HTTP header
36 | $script:User = "" # Not needed when using PAT, can be set to anything
37 | $script:Base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $User,$PAT)))
38 |
39 | Function Get-BuildDefinitionsByEnvironment {
40 | <#
41 | .OUTPUT
42 | DefinitionIDs
43 | #>
44 | [cmdletbinding()]
45 | Param(
46 | )
47 | Try{
48 | # https://docs.microsoft.com/en-us/rest/api/vsts/build/definitions/get
49 | [uri] $script:Uri = $env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI + $env:SYSTEM_TEAMPROJECT + "/_apis/build/definitions?type=build&name=*$EnvironmentName*"
50 |
51 | # Invoke the REST call and capture the response
52 | $Response = Invoke-RestMethod -Uri $Uri `
53 | -Method Get `
54 | -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)} `
55 |
56 | "Get-BuildDefinitionsByEnvironment returned response: $Response"
57 | "Found build definitions:"
58 | $Response.value.name
59 | $script:DefinitionIDs = $Response.value.id # Scope variable to "script" to be used in other functions
60 | }
61 | Catch{
62 | $result = $_.Exception.Response.GetResponseStream()
63 | $reader = New-Object System.IO.StreamReader($result)
64 | $reader.BaseStream.Position = 0
65 | $reader.DiscardBufferedData()
66 | $responseBody = $reader.ReadToEnd();
67 | $responseBody
68 | Exit 1
69 | }
70 | }
71 |
72 | Function Update-BuildDefinitionTriggerPath {
73 | [cmdletbinding()]
74 | Param(
75 | )
76 | Try{
77 | # https://docs.microsoft.com/en-us/rest/api/vsts/build/definitions/update
78 | [uri] $script:Uri = $env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI + $env:SYSTEM_TEAMPROJECT + "/_apis/build/definitions/$DefinitionID" + "?api-version=3.0"
79 |
80 | # Get definition to update, use response for json body to update definition
81 | $Definition = Invoke-RestMethod -Uri $Uri `
82 | -Method Get `
83 | -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)}
84 |
85 | If($Definition.triggers.pathFilters){
86 | $DefinitionTriggerPaths = $Definition.triggers.pathFilters
87 | }
88 | Else{
89 | "No path filter, moving on to next definition"
90 | continue # move to next definition in foreach loop
91 | }
92 | # Convert response to JSON to be used in Put below
93 | $Definition = $Definition | ConvertTo-Json -Depth 100
94 |
95 | ForEach($Path in $DefinitionTriggerPaths){
96 | # Replace first character in path filter: + include, - exclude
97 | # Example: +$/ED/SCM
98 | Switch($Action.ToLower()){
99 | "enable" {
100 | $PathAfter = $Path.Replace("-$/","+$/")
101 | }
102 | "disable" {
103 | $PathAfter = $Path.Replace("+$/","-$/")
104 | }
105 | default {
106 | Write-Error "Not a valid action. Actions available: Enable, Disable"
107 | }
108 | }
109 |
110 | # replace old trigger path with new
111 | $Definition = $Definition.Replace("$Path","$PathAfter")
112 |
113 | }
114 |
115 | # Use updated response to update definition
116 | $UpdatedDefinition = Invoke-RestMethod -Uri $Uri `
117 | -Method Put `
118 | -ContentType application/json `
119 | -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)} `
120 | -Body $Definition
121 |
122 | "Trigger updated:"
123 | $UpdatedDefinition.triggers
124 | }
125 | Catch{
126 | $result = $_.Exception.Response.GetResponseStream()
127 | $reader = New-Object System.IO.StreamReader($result)
128 | $reader.BaseStream.Position = 0
129 | $reader.DiscardBufferedData()
130 | $responseBody = $reader.ReadToEnd();
131 | $responseBody
132 | Exit 1
133 | }
134 | }
135 |
136 | Try
137 | {
138 | "Getting build definitions for environment: $EnvironmentName..."
139 | Get-BuildDefinitionsByEnvironment
140 |
141 | "$Action build definition triggers..."
142 | ForEach($DefinitionID in $DefinitionIDs){
143 | "$Action trigger for $DefinitionID"
144 | Update-BuildDefinitionTriggerPath
145 | }
146 | }
147 | Catch
148 | {
149 | Write-Error $_
150 | Exit 1
151 | }
--------------------------------------------------------------------------------
/TFS_UpdateWorkItemsWithBuildLink.ps1:
--------------------------------------------------------------------------------
1 | # Adds build link to associate work items
2 | # Runs as the last step in build definitions
3 |
4 | # Encrypted password passed via build definition
5 | param(
6 | [string]$passwd
7 | )
8 |
9 | Function UpdateWorkItemsWithBuildLink
10 | {
11 | $user = ""
12 | $secpasswd = ConvertTo-SecureString $passwd -AsPlainText -Force
13 | $credential = New-Object System.Management.Automation.PSCredential($user, $secpasswd)
14 |
15 | # Uri to get associated work items: https://www.visualstudio.com/en-us/integrate/api/build/builds#GetbuilddetailsWorkitems
16 | [uri] $BuildWorkItemsUri = $tfsUrl + $env:SYSTEM_TEAMPROJECT + "/_apis/build/builds/" + $env:BUILD_BUILDID +"/workitems?api-version=2.0"
17 | write-host $BuildWorkItemsUri
18 |
19 | try
20 | {
21 | # Get build details: Workitems
22 | $results = Invoke-RestMethod -Uri $BuildWorkItemsUri -Method Get -Credential $credential
23 | write-host $results
24 | }
25 | catch
26 | {
27 | # Catch 404 and move on
28 | $_.Exception.Response.StatusCode.Value__
29 | }
30 |
31 | $WorkitemIDs = $results.value.id
32 |
33 | # List array of associated work items
34 | write-host "Associated Work Item IDs:"$WorkitemIDs
35 |
36 | # Loop through each work item and update with link to build
37 | ForEach($WorkItemID in $WorkitemIDs)
38 | {
39 | # Uri to add hyperlink to work items: https://www.visualstudio.com/integrate/api/wit/work-items#UpdateworkitemsAddalink
40 | [uri] $WorkItemLinkUri = $env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI + "_apis/wit/workitems/" + $WorkItemID +"?api-version=1.0"
41 | write-host $WorkItemLinkUri
42 |
43 | $JSONBody= @"
44 | [{
45 | "op": "add",
46 | "path": "/relations/-",
47 | "value": {
48 | "rel": "Hyperlink",
49 | "url": " $env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI$env:SYSTEM_TEAMPROJECT/_build#buildId=$env:BUILD_BUILDID&_a=summary"
50 | }
51 | }]
52 | "@
53 | # Add a link to each work item with build link
54 | Invoke-RestMethod -Uri $WorkItemLinkUri -Method Patch -ContentType application/json-patch+json -Body $JSONBody -Credential $credential
55 | }
56 | }
57 |
58 | Try
59 | {
60 | UpdateWorkItemsWithBuildLink
61 | }
62 | Catch
63 | {
64 | Write-Host "Exception Type: $($_.Exception.GetType().FullName)"
65 | Write-Host "Exception Message: $($_.Exception.Message)"
66 | }
67 |
--------------------------------------------------------------------------------
/Update-AzDevOpsBuildAgentPools-.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .Description
3 | Script used to bulk update Azure Pipeline build definition's agent pools to Default
4 | #>
5 |
6 | $PAT = ""
7 | $AzureDevOpsOrgURL = "" # https://dev.azure.com/{organization}
8 |
9 | # Base64-encodes the Personal Access Token (PAT) appropriately
10 | # This is required to pass PAT through HTTP header
11 | $script:User = "" # Not needed when using PAT, can be set to anything
12 | $script:Base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $User,$PAT)))
13 |
14 | # Get all projects in organization
15 | [uri] $script:GetProjectsUri = "$AzureDevOpsOrgURL/_apis/projects`?api-version=5.1"
16 | $GetProjectsResponse = Invoke-RestMethod -Uri $GetProjectsUri -Method Get -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)}
17 |
18 | # Get name of projects from response object
19 | $Projects = $GetProjectsResponse.value.name
20 |
21 | # Loop through each project and update it's build and Build definitions
22 | ForEach($Project in $Projects){
23 | "Updating definitions in Project: $Project"
24 |
25 | # Get Queue ID for project
26 | [uri] $script:GetQueueURI = "$AzureDevOpsOrgURL/$Project/_apis/distributedtask/queues"
27 | $GetQueueResponse = Invoke-RestMethod -Uri $GetQueueURI -Method GET -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)}
28 |
29 | $QueueID = $GetQueueResponse.value | Where-Object {$_.name -eq "Default"}
30 | $QueueID = $QueueID.id
31 |
32 | If($QueueID){
33 |
34 | # Get all Build definitions in project
35 | [uri] $script:GetBuildDefinitionsUri = "$AzureDevOpsOrgURL/$Project/_apis/build/definitions"
36 |
37 | Try{
38 | $GetBuildDefinitionsResponse = Invoke-RestMethod -Uri $GetBuildDefinitionsUri -Method Get -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)}
39 | }
40 | Catch{
41 | "Did not find Builds in project: $Project"
42 | Continue
43 | }
44 | # Get definition ID's from response object
45 | $DefinitionIDs = $GetBuildDefinitionsResponse.value.id
46 |
47 | # Loop through and request each definition
48 | ForEach($DefinitionID in $DefinitionIDs){
49 |
50 | [uri] $script:GetDefinitionURI = "$AzureDevOpsOrgURL/$Project/_apis/build/Definitions/$DefinitionID"
51 | $GetDefinitionResponse = Invoke-RestMethod -Uri $GetDefinitionURI -Method GET -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)}
52 |
53 | # Set Agent pool and all properties to default
54 | $GetDefinitionResponse.queue | Add-Member -MemberType NoteProperty -Name 'id' -Value $QueueID -Force
55 | $GetDefinitionResponse.queue | Add-Member -MemberType NoteProperty -Name 'name' -Value "Default" -Force
56 | $GetDefinitionResponse.queue | Add-Member -MemberType NoteProperty -Name 'url' -Value "$AzureDevOpsOrgURL/_apis/build/Queues/$QueueID" -Force
57 | If($GetDefinitionResponse.process.phases.target.queue){$GetDefinitionResponse.process.phases.target.queue | Add-Member -MemberType NoteProperty -Name 'id' -Value $QueueID -Force}
58 | If($GetDefinitionResponse.process.phases.target.queue){$GetDefinitionResponse.process.phases.target.queue | Add-Member -MemberType NoteProperty -Name 'url' -Value "$AzureDevOpsOrgURL/_apis/build/Queues/$QueueID" -Force}
59 | $GetDefinitionResponse.queue | Add-Member -MemberType NoteProperty -Name 'pool' -Value "" -Force
60 | $GetDefinitionResponse.queue.pool | Add-Member -MemberType NoteProperty -Name 'id' -Value 1 -Force
61 | $GetDefinitionResponse.queue.pool | Add-Member -MemberType NoteProperty -Name 'name' -Value "Default" -Force
62 | If($GetDefinitionResponse.queue.pool.isHosted){$GetDefinitionResponse.queue.pool.isHosted = $false}
63 | If($GetDefinitionResponse.queue.pool.isHosted){$GetDefinitionResponse.process.target.agentSpecification = ""}
64 |
65 | # Convert response to JSON to be used in Put below
66 | $Definition = $GetDefinitionResponse | ConvertTo-Json -Depth 100
67 |
68 | $Definition = $Definition -replace "build/Queues/\d+", "build/Queues/$QueueID"
69 |
70 | # Use updated response to update definition
71 | # Note: Build Definition ID is not needed in URI for PUT
72 | $script:UpdateDefinitionURI = "$AzureDevOpsOrgURL/$Project/_apis/Build/definitions/$DefinitionID`?api-version=5.1"
73 | Invoke-RestMethod -Uri $UpdateDefinitionURI -Method Put -ContentType application/json -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)} -Body $Definition
74 | }
75 | }
76 | Else{
77 | "Did not find Default queue in project: $Project"
78 | }
79 | }
80 |
81 |
82 |
--------------------------------------------------------------------------------
/Update-AzDevOpsReleaseAgentPools.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .Description
3 | Script used to bulk update Azure Pipeline release definition's and set agent pools to Default
4 | #>
5 |
6 | $PAT = "6up2qi26kkrcetyqruudggvbqwj75f7t4jw2bcjbkcno3yiisceq"
7 | $AzureDevOpsOrgURL = "https://dev.azure.com/MSFT-MarcusFelling" # https://dev.azure.com/{organization}
8 | $AzureDevOpsVSRMURL = $AzureDevOpsOrgURL -replace "dev.azure.com", "vsrm.dev.azure.com" # Add vsrm (visual studio release management) to URI
9 |
10 | # Base64-encodes the Personal Access Token (PAT) appropriately
11 | # This is required to pass PAT through HTTP header
12 | $script:User = "" # Not needed when using PAT, can be set to anything
13 | $script:Base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $User,$PAT)))
14 |
15 | # Get all projects in organization
16 | [uri] $script:GetProjectsUri = "$AzureDevOpsOrgURL/_apis/projects`?api-version=5.1"
17 | $GetProjectsResponse = Invoke-RestMethod -Uri $GetProjectsUri -Method Get -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)}
18 |
19 | # Get name of projects from response object
20 | $Projects = $GetProjectsResponse.value.name
21 |
22 | # Loop through each project and update it's build and release definitions
23 | ForEach($Project in $Projects){
24 | "Updating definitions in Project: $Project"
25 |
26 | # Get Queue ID for project
27 | [uri] $script:GetQueueURI = "$AzureDevOpsOrgURL/$Project/_apis/distributedtask/queues"
28 | $GetQueueResponse = Invoke-RestMethod -Uri $GetQueueURI -Method GET -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)}
29 |
30 | $QueueID = $GetQueueResponse.value | Where-Object {$_.name -eq "Default"}
31 | $QueueID = $QueueID.id
32 |
33 | If($QueueID){
34 |
35 | # Get all release definitions in project
36 | [uri] $script:GetReleaseDefinitionsUri = "$AzureDevOpsVSRMURL/$Project/_apis/Release/definitions"
37 |
38 | Try{
39 | $GetReleaseDefinitionsResponse = Invoke-RestMethod -Uri $GetReleaseDefinitionsUri -Method Get -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)}
40 | }
41 | Catch{
42 | "Did not find releases in project: $Project"
43 | Continue
44 | }
45 | # Get definition ID's from response object
46 | $DefinitionIDs = $GetReleaseDefinitionsResponse.value.id
47 |
48 | # Loop through and request each definition
49 | ForEach($DefinitionID in $DefinitionIDs){
50 |
51 | [uri] $script:GetDefinitionURI = "$AzureDevOpsVSRMURL/$Project/_apis/release/definitions/$DefinitionID`?`$expand=Environments"
52 | $GetDefinitionResponse = Invoke-RestMethod -Uri $GetDefinitionURI -Method GET -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)}
53 |
54 | # Convert response to JSON to be used in Put below
55 | $Definition = $GetDefinitionResponse | ConvertTo-Json -Depth 100
56 |
57 | # Set Agent Pool to Default if 0
58 | $Definition = $Definition -replace "`"queueId`": \d+", "`"queueId`": $QueueID"
59 |
60 | # Use updated response to update definition
61 | # Note: Release Definition ID is not needed in URI for PUT
62 | $script:UpdateDefinitionURI = "$AzureDevOpsVSRMURL/$Project/_apis/release/definitions`?api-version=5.0"
63 | Invoke-RestMethod -Uri $UpdateDefinitionURI -Method Put -ContentType application/json -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)} -Body $Definition
64 | }
65 | }
66 | Else{
67 | "Did not find Default queue in project: $Project"
68 | }
69 | }
70 |
71 |
72 |
--------------------------------------------------------------------------------
/Update-ReleaseDefinitionsWindowsMachineFileCopyTask.ps1:
--------------------------------------------------------------------------------
1 | $TFSBaseURL = ""
2 | $Project = ""
3 |
4 | # Base64-encodes the Personal Access Token (PAT) appropriately
5 | # This is required to pass PAT through HTTP header
6 | $script:User = "" # Not needed when using PAT, can be set to anything
7 | $script:Base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $User,$PAT)))
8 |
9 | # Get list of all release definitions
10 | [uri] $script:GetDefinitionsURI = "$TFSBaseURL/$Project/_apis/release/definitions"
11 |
12 | # Invoke the REST call and capture the response
13 | $GetDefinitionsUriResponse = Invoke-RestMethod -Uri $GetDefinitionsURI -Method Get -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)}
14 | $DefinitionIDs = $GetDefinitionsUriResponse.value.id
15 |
16 | ForEach($DefinitionID in $DefinitionIDs){
17 | [uri] $script:GetDefinitionsURI = "$TFSBaseURL/$Project/_apis/release/definitions/$DefinitionID"
18 | $GetDefinitionsUriResponse = Invoke-RestMethod -Uri $GetDefinitionsURI -Method GET -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)}
19 | $FileCopyTasks = $GetDefinitionsUriResponse.environments.deployPhases.workflowTasks | Where-Object refName -like "WindowsMachineFileCopy*"
20 | ForEach($Task in $FileCopyTasks){
21 | # Update task to v2
22 | $Task.version = '2.*'
23 |
24 | # Bug #1 MachineName was lost in upgrade, add it back using EnvironmentName
25 | $Task.inputs.MachineNames = $Task.inputs.EnvironmentName
26 | }
27 | }
28 |
29 | # Convert response to JSON to be used in Put below
30 | $Definition = $GetDefinitionsUriResponse | ConvertTo-Json -Depth 100
31 |
32 | # Use updated response to update definition
33 | $script:UpdateDefinitionURI = "$TFSBaseURL/$Project/_apis/release/definitions?api-version=5.0"
34 | $UpdatedDefinition = Invoke-RestMethod -Uri $UpdateDefinitionURI -Method Put -ContentType application/json -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)} -Body $Definition
35 |
--------------------------------------------------------------------------------
/VSTS_CreateReleasePullRequest.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Uses the VSTS REST API to create pull request
4 |
5 | .DESCRIPTION
6 | This script uses the VSTS REST API to create a Pull Request in the specified
7 | repository, source and target branches. Intended to run via VSTS Build using a build step for each repository.
8 | https://www.visualstudio.com/en-us/docs/integrate/api/git/pull-requests/pull-requests
9 |
10 | .NOTES
11 | Existing branch policies are automatically applied.
12 |
13 | .PARAMETER Repository
14 | Repository to create PR in
15 |
16 | .PARAMETER SourceRefName
17 | The name of the source branch without ref.
18 |
19 | .PARAMETER TargetRefName
20 | The name of the target branch without ref.
21 |
22 | .PARAMETER APIVersion
23 | API versions are in the format {major}.{minor}[-{stage}[.{resource-version}]] - For example, 1.0, 1.1, 1.2-preview, 2.0.
24 |
25 | .PARAMETER ReviewerGUID
26 | ID(s) of the initial reviewer(s). Not mandadory.
27 | Can be found in existing PR by using GET https://{instance}/DefaultCollection/{project}/_apis/git/repositories/{repository}/pullRequests/{pullrequestid}?api-version=3.0
28 |
29 | .PARAMETER PAT
30 | Personal Access token. It's recommended to use a service account and pass via encrypted build definition variable.
31 | #>
32 | [CmdletBinding()]
33 | Param(
34 | [Parameter(Position=0,Mandatory=$true,ValueFromPipeline)]
35 | [ValidateNotNullOrEmpty()]
36 | [string]$script:Repository,
37 | [Parameter(Position=1,Mandatory=$true,ValueFromPipeline)]
38 | [ValidateNotNullOrEmpty()]
39 | [string]$script:SourceRefName,
40 | [Parameter(Position=2,Mandatory=$true,ValueFromPipeline)]
41 | [ValidateNotNullOrEmpty()]
42 | [string]$script:TargetRefName,
43 | [Parameter(Position=3,Mandatory=$true,ValueFromPipeline)]
44 | [ValidateNotNullOrEmpty()]
45 | [string]$script:APIVersion,
46 | [Parameter(Position=4,Mandatory=$true,ValueFromPipeline)]
47 | [ValidateNotNullOrEmpty()]
48 | [int]$script:ReviewerGUID,
49 | [Parameter(Position=5,Mandatory=$false,ValueFromPipeline)]
50 | [ValidateNotNullOrEmpty()]
51 | [string]$script:PAT
52 | )
53 |
54 | Function CreatePullRequest
55 | {
56 | # Contruct Uri for Pull Requests: https://{instance}/DefaultCollection/{project}/_apis/git/repositories/{repository}/pullRequests?api-version={version}
57 | # Note: /DefaultCollection/ is required for all VSTS accounts
58 | # Environment variables are populated when running via VSTS build
59 | [uri] $PRUri = $env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI + "/DefaultCollection/" + $env:SYSTEM_TEAMPROJECT + "/_apis/git/repositories/$Repository/pullRequests?api-version=$APIVersion"
60 |
61 | # Base64-encodes the Personal Access Token (PAT) appropriately
62 | # This is required to pass PAT through HTTP header in Invoke-RestMethod bellow
63 | $User = "" # Not needed when using PAT, can be set to anything
64 | $Base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $User,$PAT)))
65 |
66 | # Prepend refs/heads/ to branches so shortened version can be used in title
67 | $Ref = "refs/heads/"
68 | $SourceBranch = "$Ref" + "$SourceRefName"
69 | $TargetBranch = "$Ref" + "$TargetRefName"
70 |
71 | # JSON for creating PR with Reviewer specified
72 | If($ReviewerGUID){
73 | $JSONBody= @"
74 | {
75 | "sourceRefName": "$SourceBranch",
76 | "targetRefName": "$TargetBranch",
77 | "title": "Merge $sourceRefName to $targetRefName",
78 | "description": "PR Created automajically via REST API ",
79 | "reviewers": [
80 | {
81 | "id": { $ReviewerGUID }
82 | }
83 | ]
84 | }
85 | "@
86 | }
87 | Else{
88 | # JSON for creating PR without Reviewer specified
89 | $JSONBody= @"
90 | {
91 | "sourceRefName": "$SourceBranch",
92 | "targetRefName": "$TargetBranch",
93 | "title": "Merge $sourceRefName to $targetRefName",
94 | "description": "PR Created automajically via REST API ",
95 | }
96 | "@
97 | }
98 |
99 | # Use URI and JSON above to invoke the REST call and capture the response.
100 | $Response = Invoke-RestMethod -Uri $PRUri `
101 | -Method Post `
102 | -ContentType "application/json" `
103 | -Headers @{Authorization=("Basic {0}" -f $Base64AuthInfo)} `
104 | -Body $JSONBody
105 |
106 | # Get new PR info from response
107 | $script:NewPRID = $Response.pullRequestId
108 | $script:NewPRURL = $Response.url
109 | }
110 |
111 | Try
112 | {
113 | "Creating PR in $Repository repository: Source branch $SourceRefName Target Branch: $TargetRefName"
114 | CreatePullRequest
115 | "Created PR $NewPRID`: $NewPRURL"
116 | }
117 | Catch
118 | {
119 | $result = $_.Exception.Response.GetResponseStream()
120 | $reader = New-Object System.IO.StreamReader($result)
121 | $reader.BaseStream.Position = 0
122 | $reader.DiscardBufferedData()
123 | $responseBody = $reader.ReadToEnd();
124 | $responseBody
125 | Exit 1 # Fail build if errors
126 | }
127 |
--------------------------------------------------------------------------------