I am trying to write a PowerShell script that allows me to update all the names of our devices in Intune [430ish devices] to reflect our asset tags. When they were imported into our tenant, they were given the serialNumber of the device as their deviceName. All permissions for the API have been applied:
API Permissions:
Device Read
Device Read all
DeviceManagementApps.ReadAll
DeviceManagementApps.ReadWriteAll
DeviceManagementConfiguration.ReadAll
DeviceManagementConfiguration.ReadWriteAll
DeviceManagementManagedDevices.PrivilegedOperations.All
DeviceManagementManagedDevices.ReadAll
DeviceManagementManagedDevices.ReadWriteAll
DeviceManagementRBAC.ReadAll
DeviceManagementRBAC.ReadWriteALL
DeviceManagementServiceConfig.ReadAll
DeviceManagementServiceConfig.ReadWriteAll
User Read
This is the code as far as I can get it, but I am still getting the following error [I apologise for ugly or poorly formatted code, I have had no formal training, all learnt using google-fu!]:
# Setting variables for connecting to the MS API
$ApplicationID = "xxxxxxxxxxxxxxxxxxxxxxxxxxx"
$TenantDomainName = "contoso.com"
$AccessSecret = Read-Host "Enter Secret"
# Connect to MSGraph command to run
Connect-MSGraph
# Setting the body of the json
$Body = #{
Grant_Type = "client_credentials"
Scope = "https://graph.microsoft.com/.default"
client_Id = $ApplicationID
Client_Secret = $AccessSecret
}
# Authenticating the connection to MSGraph
$ConnectGraph = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$TenantDomainName/oauth2/v2.0/token" `
-Method POST -Body $Body
$token = $ConnectGraph.access_token
# Importing the CSV of device information
$csvfile = "C:\<Path to file>"
Import-Csv $csvfile | ForEach-Object {
$serialNumber = $_.serialNumber;
$tag = $_.tag;
$deviceId = $serialNumber
Write-Host "Renaming machine from: $deviceID to: $tag" -ForegroundColor Cyan
# Getting the Device from the CSV and then putting it into MSGraph compatible Json
$DeviceToRename = Get-IntuneManagedDevice -Filter ("serialNumber eq '$serialNumber'")
Foreach ($Device in $DeviceToRename) {
$Resource = "deviceManagement/managedDevices('$DeviceId')/setDeviceName"
$graphApiVersion = "Beta"
$uri = "https://graph.microsoft.com/beta/deviceManagement/managedDevices/executeAction"
#This JSON format doesnt work
# $JSONPayload = #"
# { <NEW>
# "body": <NEW>
# {
# action: "setDeviceName",
# actionName: "setDeviceName",
# deviceName: "$tag",
# realaction: "setDeviceName",
# restartNow: false
# }
# } <NEW>
#"#
#Don't know if this works properly either?
$JSONPayload = #"
{
"#odata.type": "#microsoft.graph.managedDevice",
"actionName": "setDeviceName",
"deviceName": "$tag"
}
"#
# Writing out to check if this is working correctly
Write-Host $JSONPayload
# Converting $JSONPayload to an actual workable JSON
$convertedJSON = ConvertTo-Json $JSONPayload
try {
Invoke-MSGraphRequest -Url $uri -HttpMethod PATCH -Body $JSONPayload -ContentType "application/Json" -Verbose
} catch {
# Dig into the exception to get the Response details.
Write-Host "StatusCode:" "$_.Exception.Response.StatusCode.value__"
Write-Host "StatusDescription:" "$_.Exception.Response.StatusDescription"
Write-Host "StatusCode2:" "$_.ErrorDetails.Message"
}
}
}
Error response:
StatusCode: A parameter cannot be found that matches parameter name 'Body'..Exception.Response.StatusCode.value__
StatusDescription: A parameter cannot be found that matches parameter name 'Body'..Exception.Response.StatusDescription
StatusCode2: A parameter cannot be found that matches parameter name 'Body'..ErrorDetails.Message
Thanks
Tom
I had similar problems some months ago manipulating intune devices from an powershell runbook over graph. In my case the json body was the problem. I had to define the body first as hashtable and then convert it to json. Try something like this:
# JSONPayload as hashtable instead of string
$JSONPayload = #{
"#odata.type" = "#microsoft.graph.managedDevice"
"actionName" = "setDeviceName"
"deviceName" = "$tag"
}
# Writing out to check if this is working correctly
$JSONPayload
# Converting $JSONPayload to an actual workable JSON
$convertedJSON = $JSONPayload | ConvertTo-Json
And then pass the $convertedJSON to your graph call as body:
Invoke-MSGraphRequest -Url $uri -HttpMethod POST -Content $convertedJSON -Verbose
EDIT:
You are calling the endpoint /deviceManagement/managedDevices/executeAction with the http method PATCH. According to this ms docs article you have to call the endpoint with the http method POST.
Related
I have a URL health-checking PowerShell script which correctly gets an HTTP 200 status code on most of my intranet sites, but a '0' status code is returned on a small minority of them. The '0' code is an API return rather than from the web site itself, according to my research of questions from others who have written similar URL-checking PowerShell scripts. Thinking this must be a timeout issue, where API returns '0' before the slowly-responding web site returns its 200, I've researched yet more questions about this subject area on SO and implemented a suggestion from someone to insert a timeout in the script. The timeout setting though, no matter how high I set the timeout value, doesn't help. I still get the same '0' "response" code from the same web sites even though those web sites are up and running as checked from any regular web browser. Any thoughts on how I could tweak the timeout setting in the script below in order to get the correct 200 response code?
The Script:
$URLListFile = "C:\Users\Admin1\Documents\Scripts\URL Check\URL_Check.txt"
$URLList = Get-Content $URLListFile -ErrorAction SilentlyContinue
#if((test-path $reportpath) -like $false)
#{
#new-item $reportpath -type file
#}
#For every URL in the list
$result = foreach($Uri in $URLList) {
try{
#For proxy systems
[System.Net.WebRequest]::DefaultWebProxy = [System.Net.WebRequest]::GetSystemWebProxy()
[System.Net.WebRequest]::DefaultWebProxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
#Web request
$req = [system.Net.WebRequest]::Create($uri)
$req.Timeout=5000
$res = $req.GetResponse()
}
catch {
#Err handling
$res = $_.Exception.Response
}
$req = $null
#Getting HTTP status code
$int = [int]$res.StatusCode
# output a formatted string to capture in variable $result
"$int - $uri"
#Disposing response if available
if($res){
$res.Dispose()
}
}
# output on screen
$result
#output to log file
$result | Set-Content -Path "C:\Users\Admin1\Documents\Scripts\z_Logs\URL_Check\URL_Check_log.txt" -Force
Current output:
200 - http://192.168.1.1/
200 - http://192.168.1.2/
200 - http://192.168.1.250/config/authentication_page.htm
0 - https://192.168.1.50/
200 - http://app1-vip-http.dev.local/
0 - https://CA/certsrv/Default.asp
Perhaps using PowerShell cmdlet Invoke-WebRequest works better for you. It has many more parameters and switches to play around with like ProxyUseDefaultCredentials and DisableKeepAlive
$pathIn = "C:\Users\Admin1\Documents\Scripts\URL Check\URL_Check.txt"
$pathOut = "C:\Users\Admin1\Documents\Scripts\z_Logs\URL_Check\URL_Check_log.txt"
$URLList = Get-Content -Path $pathIn
$result = foreach ($uri in $URLList) {
try{
$res = Invoke-WebRequest -Uri $uri -UseDefaultCredentials -UseBasicParsing -Method Head -TimeoutSec 5 -ErrorAction Stop
$status = [int]$res.StatusCode
}
catch {
$status = [int]$_.Exception.Response.StatusCode.value__
}
# output a formatted string to capture in variable $result
"$status - $uri"
}
# output on screen
$result
#output to log file
$result | Set-Content -Path $pathOut -Force
I am able to kick off an Azure Devops Build job and send a text parameter but I only seem to be able to send a single parameter and not more.
I need to send a token parameter Ok that works but I also want to send a text param that contains a json payload to be processed by a Powershell script in the Build job.
So I have a Hellow World definition setup with two variables in Pipeline variables.
First one is tokentext the second one is jsonInput.
Both have the checkbox "Settable at queue time" checked.
I have a PowerShell Inline script in the job definition with this:
Write-Host "Hello World"
Write-Host "tokentext: $(tokentext) `n"
Write-Host "Json Input"
Write-Host "---------------------------------------------------"
$(jsonInput)
My Body that I am sending to the Invoke-RestMethod is:
$body = #{
definition = #{
id = $buildDefID
}
parameters = "{`"tokentext`" :$mytoken}
{`"jsonInput`" :$j}
"
}
$b = $body | ConvertTo-Json
This works but the above does not:
$body = #{
definition = #{
id = $buildDefID
}
parameters = "{`"tokentext`" :$mytoken}
"
}
$b = $body | ConvertTo-Json
I have tried it with a comma separating the parameters etc.. All kids of things I have tried. I could sure use some assistance if anyone is sening multiple parameters to variables in a build Definition from a script.
Thanks
Below request body works for me. Please check it out.
$body = #{
definition= #{id = $buildDefID};
parameters ="{`"jsonInput`":`"$jsonInput`", `"tokentext`":`"$tokentext`"}"
}
$b = $body | ConvertTo-Json
You can also run your pipeline via Runs - Run Pipeline rest api which is less complicated.
POST https://dev.azure.com/{organization}/{project}/_apis/pipelines/{pipelineId}/runs?api-version=6.1-preview.1
pipelineId is the $buildDefID
You can put the parameters in your request body as below:
$body=#{
variables = #{
jsonInput= #{value = $jsonInput};
tokentext= #{value = $tokentext}
}
}
$b = $body | ConvertTo-Json
Please try this:
$body = #{
definition = #{
id = $buildDefID
}
parameters = "{`"tokentext`" :$mytoken, `"jsonInput`" :$j}"
}
$b = $body | ConvertTo-Json
I have a function which gets data from all APs in a given store.
Function Get-AllAP {
Write-Verbose "Function Start: Get-AllAP"
Write-Host "Getting all Access Points in Store $Store .."
Write-Host " "
Write-Verbose "Getting all APs for Store $Store"
$storeApReq = "https://cpist/webacs/api/v3/data/AccessPointDetails.json?.group=$Store"
Write-Verbose "Making request to $storeApReq"
$Global:apIdListReq = Invoke-RestMethod -uri $storeApReq -method Get -ContentType 'application/json' -headers #{ Authorization = $auth }
$Global:apIdList = $apIdListReq.queryResponse.entityId
$Global:apIdCount = $apIdListReq.queryResponse."#count"
Write-Verbose "Found $siteAPCount APs in Sites Database. $apIdCount out of $siteAPCount APs found."
Write-Verbose "Response Received: $apIdList"
$Global:apIdURL = $apIdListReq.queryResponse.entityId
$Global:apURLs = $apIdListReq.queryResponse.entityId | ForEach-Object -MemberName '#url'
Write-Verbose "Looping through APs."
$Global:apLoop = ForEach($apURL in $apURLs) {
$apFullReq = Invoke-RestMethod -uri $apURL'.json' -Method Get -ContentType 'application/json' -headers #{ Authorization = $auth }
Write-Verbose "Response: $apFullReq"
$Global:allApData = $apFullReq.queryResponse.entity.accessPointDetailsDTO
## Format ALL AP data
$apStatus =$allApData.status
$apName = $allApData.name
$apPing = $allApData.reachabilitystatus
## Format data
Write-Host $apName
Write-Verbose $apStatus
Write-Verbose $apPing
## Output data
Write-Host " "
Write-Host "AP Name: $apName"
Write-Host "AP Status: $apStatus"
Write-Host "AP Ping: $apPing"
}
## Clear
## Give option to reset AP
$doReset = Read-Host "Type 'reset' to reset all Access Points in Store $Store"
IF($doReset-eq 'reset') {
Manage-APResetStore
} else {
Repeat
}
After this function is run, the user is given a choice to Repeat or reset using Manage-APResetStore which is as follows;
Function Manage-APResetStore {
Write-Verbose "Function started: Manage-APResetStore"
## Create our batch job
ForEach($apName in $allAPData) {
Write-Host $apName
}
## Send our job
}
This is what it outputs currently, which shows the full JSON for $allAPData rather than $apName.
VERBOSE: Function started: Manage-APResetStore
Establishing SSH connection to Cisco Controller dc1flexwlc02
#{#displayName=16162527426; #id=16162527426; adminStatus=ENABLE; apType=AP3500I; cdpNeighbors=; clientCount=3; clientC
ount_2_4GHz=0; clientCount_5GHz=3; ethernetMac=50:57:a8:a1:7c:38; ipAddress=10.0.126.69; locationHierarchy=Dicks Sport
ing Goods > 0026 > 1st floor; macAddress=5c:50:15:1c:43:30; mapLocation=default location; model=AIR-CAP3502I-A-K9; nam
e=0026AP5; reachabilityStatus=REACHABLE; serialNumber=FTX1611E55H; softwareVersion=8.5.131.0; status=CLEARED; type=Uni
fiedAp; unifiedApInfo=; upTime=68916194}
VERBOSE: Function start: Check-DevMode
VERBOSE: Function Start: Execute-Application
What the Manage-APResetStore function is meant to do is send a list of "reset" commands to the controller for every AP that exists in that store. So what I want to do, is make a list out of my ForEach loop that I can use to generate a batch job to send to the controller.
To be able to loop through again after the data is formatted, I created a new array with just the apName objects so we can use it in other functions.
Create the array outside the loop:
## Create array object
$apArray = New-Object System.Collections.ArrayList
Add the apName object to the array:
## Format ALL AP data
$apStatus =$allApData.status
$apName = $allApData.name
$apPing = $allApData.reachabilitystatus
## Format data
Write-Output $apName
Write-Output $apStatus
Write-Output $apPing
## Output data to array
$apArray.Add($apName)
}
Write-Host $apArray
## Clear
## Give option to reset AP
$doReset = Read-Host "Type 'reset' to reset all Access Points in Store $Store"
Using the array:
## Loop through our commands
ForEach($apName in $apArray) {
$stream.WriteLine("show loginsessions $apName")
sleep 1
$stream.WriteLine('y')
sleep 2
$stream.Read()
Write-Host "$apName has been reset"
}
Try replacing your ForEach section as follows:
ForEach ($apName in $allApData.ApName)
This will mean that $apName within the loop is just the apName property, and not the entire part of $allApData.
I can't find any clue about it except a deprecated article using tweetpic which is now closed.
Any alternative that works in Powershell ?
Update: my question is not about if there is a Twitter API of course I know there is, but as it is not trivial to use like this Powershell Guy who is stuck https://twittercommunity.com/t/media-upload-doesnt-work-powershell/93861 I'm looking for a module.
Credit to Adam Bertram from https://www.adamtheautomator.com/twitter-module-powershell/.
Disclaimer: this only allows to post tweets and DM.
You can try using the PSM1 module below, but you need to create your own Twitter application on apps.twitter.com and generate an access token under the API keys section of the application. Once you do so, I recommend copying/pasting your API key, API secret, access token and access token secret as default parameters under the Get-OAuthAuthorization function.
<#
===========================================================================
Created on: 8/31/2014 3:11 PM
Created by: Adam Bertram
Filename: MyTwitter.psm1
------------------------------------------------------------------------
===========================================================================
#>
function Get-OAuthAuthorization {
<#
.SYNOPSIS
This function is used to setup all the appropriate security stuff needed to issue
API calls against Twitter's API. It has been tested with v1.1 of the API. It currently
includes support only for sending tweets from a single user account and to send DMs from
a single user account.
.EXAMPLE
Get-OAuthAuthorization -DmMessage 'hello' -HttpEndPoint 'https://api.twitter.com/1.1/direct_messages/new.json' -Username adam
This example gets the authorization string needed in the HTTP POST method to send a direct
message with the text 'hello' to the user 'adam'.
.EXAMPLE
Get-OAuthAuthorization -TweetMessage 'hello' -HttpEndPoint 'https://api.twitter.com/1.1/statuses/update.json'
This example gets the authorization string needed in the HTTP POST method to send out a tweet.
.PARAMETER HttpEndPoint
This is the URI that you must use to issue calls to the API.
.PARAMETER TweetMessage
Use this parameter if you're sending a tweet. This is the tweet's text.
.PARAMETER DmMessage
If you're sending a DM to someone, this is the DM's text.
.PARAMETER Username
If you're sending a DM to someone, this is the username you'll be sending to.
.PARAMETER ApiKey
The API key for the Twitter application you previously setup.
.PARAMETER ApiSecret
The API secret key for the Twitter application you previously setup.
.PARAMETER AccessToken
The access token that you generated within your Twitter application.
.PARAMETER
The access token secret that you generated within your Twitter application.
#>
[CmdletBinding(DefaultParameterSetName = 'None')]
[OutputType('System.Management.Automation.PSCustomObject')]
param (
[Parameter(Mandatory)]
[string]$HttpEndPoint,
[Parameter(Mandatory, ParameterSetName = 'NewTweet')]
[string]$TweetMessage,
[Parameter(Mandatory, ParameterSetName = 'DM')]
[string]$DmMessage,
[Parameter(Mandatory, ParameterSetName = 'DM')]
[string]$Username,
[Parameter()]
[string]$ApiKey = '2R3aJXohHmSABPaiQGaeprny7',
[Parameter()]
[string]$ApiSecret = '',
[Parameter()]
[string]$AccessToken = '',
[Parameter()]
[string]$AccessTokenSecret = ''
)
begin {
$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
Set-StrictMode -Version Latest
try {
[Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null
[Reflection.Assembly]::LoadWithPartialName("System.Net") | Out-Null
} catch {
Write-Error $_.Exception.Message
}
}
process {
try {
## Generate a random 32-byte string. I'm using the current time (in seconds) and appending 5 chars to the end to get to 32 bytes
## Base64 allows for an '=' but Twitter does not. If this is found, replace it with some alphanumeric character
$OauthNonce = [System.Convert]::ToBase64String(([System.Text.Encoding]::ASCII.GetBytes("$([System.DateTime]::Now.Ticks.ToString())12345"))).Replace('=', 'g')
Write-Verbose "Generated Oauth none string '$OauthNonce'"
## Find the total seconds since 1/1/1970 (epoch time)
$EpochTimeNow = [System.DateTime]::UtcNow - [System.DateTime]::ParseExact("01/01/1970", "dd/MM/yyyy", $null)
Write-Verbose "Generated epoch time '$EpochTimeNow'"
$OauthTimestamp = [System.Convert]::ToInt64($EpochTimeNow.TotalSeconds).ToString();
Write-Verbose "Generated Oauth timestamp '$OauthTimestamp'"
## Build the signature
$SignatureBase = "$([System.Uri]::EscapeDataString($HttpEndPoint))&"
$SignatureParams = #{
'oauth_consumer_key' = $ApiKey;
'oauth_nonce' = $OauthNonce;
'oauth_signature_method' = 'HMAC-SHA1';
'oauth_timestamp' = $OauthTimestamp;
'oauth_token' = $AccessToken;
'oauth_version' = '1.0';
}
if ($TweetMessage) {
$SignatureParams.status = $TweetMessage
} elseif ($DmMessage) {
$SignatureParams.screen_name = $Username
$SignatureParams.text = $DmMessage
}
## Create a string called $SignatureBase that joins all URL encoded 'Key=Value' elements with a &
## Remove the URL encoded & at the end and prepend the necessary 'POST&' verb to the front
$SignatureParams.GetEnumerator() | sort name | foreach {
Write-Verbose "Adding '$([System.Uri]::EscapeDataString(`"$($_.Key)=$($_.Value)&`"))' to signature string"
$SignatureBase += [System.Uri]::EscapeDataString("$($_.Key)=$($_.Value)&".Replace(',','%2C').Replace('!','%21'))
}
$SignatureBase = $SignatureBase.TrimEnd('%26')
$SignatureBase = 'POST&' + $SignatureBase
Write-Verbose "Base signature generated '$SignatureBase'"
## Create the hashed string from the base signature
$SignatureKey = [System.Uri]::EscapeDataString($ApiSecret) + "&" + [System.Uri]::EscapeDataString($AccessTokenSecret);
$hmacsha1 = new-object System.Security.Cryptography.HMACSHA1;
$hmacsha1.Key = [System.Text.Encoding]::ASCII.GetBytes($SignatureKey);
$OauthSignature = [System.Convert]::ToBase64String($hmacsha1.ComputeHash([System.Text.Encoding]::ASCII.GetBytes($SignatureBase)));
Write-Verbose "Using signature '$OauthSignature'"
## Build the authorization headers using most of the signature headers elements. This is joining all of the 'Key=Value' elements again
## and only URL encoding the Values this time while including non-URL encoded double quotes around each value
$AuthorizationParams = $SignatureParams
$AuthorizationParams.Add('oauth_signature', $OauthSignature)
## Remove any API call-specific params from the authorization params
$AuthorizationParams.Remove('status')
$AuthorizationParams.Remove('text')
$AuthorizationParams.Remove('screen_name')
$AuthorizationString = 'OAuth '
$AuthorizationParams.GetEnumerator() | sort name | foreach { $AuthorizationString += $_.Key + '="' + [System.Uri]::EscapeDataString($_.Value) + '", ' }
$AuthorizationString = $AuthorizationString.TrimEnd(', ')
Write-Verbose "Using authorization string '$AuthorizationString'"
$AuthorizationString
} catch {
Write-Error $_.Exception.Message
}
}
}
function Send-Tweet {
<#
.SYNOPSIS
This sends a tweet under a username.
.EXAMPLE
Send-Tweet -Message 'hello, world'
This example will send a tweet with the text 'hello, world'.
.PARAMETER Message
The text of the tweet.
#>
[CmdletBinding()]
[OutputType('System.Management.Automation.PSCustomObject')]
param (
[Parameter(Mandatory)]
[ValidateLength(1, 140)]
[string]$Message
)
process {
$HttpEndPoint = 'https://api.twitter.com/1.1/statuses/update.json'
$AuthorizationString = Get-OAuthAuthorization -TweetMessage $Message -HttpEndPoint $HttpEndPoint
## Convert the message to a Byte array
#$Body = [System.Text.Encoding]::ASCII.GetBytes("status=$Message");
$Body = "status=$Message"
Write-Verbose "Using POST body '$Body'"
Invoke-RestMethod -URI $HttpEndPoint -Method Post -Body $Body -Headers #{ 'Authorization' = $AuthorizationString } -ContentType "application/x-www-form-urlencoded"
}
}
function Send-TwitterDm {
<#
.SYNOPSIS
This sends a DM to another Twitter user. NOTE: You can only send up to
250 DMs in a 24 hour period.
.EXAMPLE
Send-TwitterDm -Message 'hello, Adam' -Username 'adam','bill'
This sends a DM with the text 'hello, Adam' to the username 'adam' and 'bill'
.PARAMETER Message
The text of the DM.
.PARAMETER Username
The username(s) you'd like to send the DM to.
#>
[CmdletBinding()]
[OutputType('System.Management.Automation.PSCustomObject')]
param (
[Parameter(Mandatory)]
[ValidateLength(1, 140)]
[string]$Message,
[Parameter(Mandatory)]
[string[]]$Username
)
process {
$HttpEndPoint = 'https://api.twitter.com/1.1/direct_messages/new.json'
## Convert the message to a Byte array
#$Message = [System.Uri]::EscapeDataString($Message)
foreach ($User in $Username) {
$AuthorizationString = Get-OAuthAuthorization -DmMessage $Message -HttpEndPoint $HttpEndPoint -Username $User -Verbose
$User = [System.Uri]::EscapeDataString($User)
$Body ="text=$Message&screen_name=$User"
Write-Verbose "Using POST body '$Body'"
Invoke-RestMethod -URI $HttpEndPoint -Method Post -Body $Body -Headers #{ 'Authorization' = $AuthorizationString } -ContentType "application/x-www-form-urlencoded"
}
}
}
Export-ModuleMember Send-Tweet
Export-ModuleMember Send-TwitterDm
I want to add a tag to a TFS project using the REST API in Powershell.
I am trying to make this request based on the documentation for
Visual Studio Integration
I am calling this:
[void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.TeamFoundation.Client')
[void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.TeamFoundation.WorkItemTracking.Client')
if ( (Get-PSSnapin -Name "Microsoft.TeamFoundation.Powershell" -ErrorAction SilentlyContinue) -eq $null )
{
Add-PSSnapin "Microsoft.TeamFoundation.Powershell"
}
$SrcCollectionUrl = 'http://tfs.myCompany.com:8080/tfs/MyCollection'
$SrcProjectName = 'myProject'
[psobject] $tfs = [Microsoft.TeamFoundation.Client.TeamFoundationServerFactory]::GetServer($SrcCollectionUrl)
[Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStoreFlags]$WorkItemBypass = [Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStoreFlags]::BypassRules
$tfstp = New-Object Microsoft.TeamFoundation.Client.TfsTeamProjectCollection($SrcCollectionUrl)
$WorkItemStore = New-Object -TypeName 'Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore' -ArgumentList $tfs.TfsTeamProjectCollection, $WorkItemBypass
$SrcProject = $WorkItemStore.Projects[$SrcProjectName]
$ProjectGuid = Split-Path $SrcProject.Uri -Leaf
$AddTagsUrl = '{0}/_apis/tagging/scopes/{1}/tags?api-version=1.0' -f $SrcCollectionUrl,$ProjectGuid
$newTagParams = #{name="PWCreateTag2"}
$outjson = $newTagParams | ConvertTo-Json
$nresp = Invoke-RestMethod -Method POST -Uri $AddTagsUrl -UseDefaultCredentials -Body $outjson -ContentType 'application/json'
Everything works. The first time. However the documentation states: "If a tag by that name already exists, no tag is created. Instead, the response body includes the existing tag with that name."
The 2nd time I call the line I get: "The remote server returned an error: (400) Bad Request."
Anyone have any Idea why this fails the 2nd time?
FYI: TFS Server is 2015, Powershell is V4
I created powershell module for this - tfs
To add tags:
'tag1','tag2' | % { Add-TFSBuildTag -Id 520 -Tag $_ }