Powershell if then - sql

I'm having an issue with the if then.
$result = getMachineInfo $rlprddeploy $mdt
"-MachineID- -LastContactAt- -LastIP- -Deployment Receiver Version-"
$machine = $result.MachineID
$result | % { '{0,-10} {1,23} {2,16} {3,20}' -f $_.MachineID, $_.LastContactAt, $_.LastIP, $_.DeploymentReceiverVersion }
#$result | ft -auto
if $_.DeploymentReceiverVersion is less than 5.46.54 then I'm going to send a file to do an update. If the version is 5.55 then nothing needs to be done.
I've been banging my head against this for a week now and I can't figure it out. Every $_.blahblah is information pulled from an SQL table on a server.
We've been sending out updates manually and I'd like to stop that.

PowerShell's If statement can be written just like any C like language:
if ($true) {Write-Host "True Yo" }
That said, you haven't posted exactly what your issues is, so this somewhat generic:
Creating a custom object:
$dep = New-Object psobject -Property #{DeploymentReceiverVersion = "5.000.11"; AppName = "OxenTails" }
Comparing this is going to pose a potential problem:
$dep.DeploymentReceiverVersion.GetType()
The version is a string, so the comparison is comparing strings:
$dep.DeploymentReceiverVersion -lt "5.000.12"
gives an expected result, but
$dep.DeploymentReceiverVersion -lt "5.000.101"
does not.
This however could work to compare each set of numbers:
$parts = $dep.DeploymentReceiverVersion.Split('.')
if ([int]$parts[0] -lt 6 -and [int]$parts[1] -lt 001 -and [int]$parts[2] -lt 101) {
#Do Stuff!
}
Additionally, as pointed out by #TheMadTechnician there is the System.Version type [Version]
It can be used with one of the overload methods:
[Version]::new(parts[0],parts[1], parts[2]) -lt [Version]::new(5,46,54)
Which is marginally more readable.

Related

How to get/set Trello custom fields using the API?

I'm already in love with the Custom Fields feature in Trello. Is there a way to get and set custom fields via the API?
I tried using the get field API call to get a field (on a board with a custom field defined called "MyCustomField"):
curl "https://api.trello.com/1/cards/57c473503a5ef0b76fddd0e5/MyCustomField?key=${TRELLO_API_KEY}&token=${TRELLO_OAUTH_TOKEN}"
to no avail.
The Custom Fields API from Trello is now officially available (announcement blog post here).
It allows users to manipulate both custom field items of boards and custom field item values on cards.
Custom Fields API documentation
Getting customFieldItems For Cards
Setting & Updating CustomFieldItems
This is just to add to bdwakefield's answer. His solution involves hard coding the names of the field ids.
If you want to also retrieve the name of the fields themselves (for example get that "ZIn76ljn-4yeYvz" is actually "priority" in Trello, without needing to hard code it, call the following end point:
boards/{trello board id}/pluginData
This will return an array with the plugins information and in one of the array items, it will include a line along the lines of:
[value] => {"fields":[{"n":"~custom field name~:","t":0,"b":1,"id":"~custom field id that is the weird stuff at the card level~","friendlyType":"Text"}]}
So you just need to figure out the plugin for custom fields in your case, and you can retrieve the key -> value pair for the custom field name and the id associated with it.
It means that if you add / remove fields, or rename them, you can handle it at run time vs changing your code.
This will also give you the options for the custom field (when it is a dropdown like in bdwakefield's example above).
So I have a "sort of" answer to this. It requires some hackery on your part to make it work and there is more than a little manual upkeep as you add properties or values -- but it works.
I am doing this in powershell (I am NOT well versed in ps just yet and this my first really 'big' script that I have pulled together for it) since my intent is to use this with TFS Builds to automate moving some cards around and creating release notes. We are using custom fields to help us classify the card and note estimate/actual hours etc. I used this guys work as a basis for my own scripting. I am not including everything but you should be able to piece everything together.
I have left out everything with connecting to Trello and all that. I have a bunch of other functions for gettings lists, moving cards, adding comments etc. The ps module I linked above has a lot of that built in as well.
function Get-TrelloCardPluginData
{
[CmdletBinding()]
param
(
[Parameter(Mandatory,ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[Alias('Id')]
[string]$CardId
)
begin
{
$ErrorActionPreference = 'Stop'
}
process
{
try
{
$uri = "$baseUrl/cards/$CardId/pluginData?$($trelloConfig.String)"
$result = Invoke-RestMethod -Uri $uri -Method GET
return $result
}
catch
{
Write-Error $_.Exception.Message
}
}
}
You'll get data that looks like this:
#{id=582b5ec8df1572e572411513; idPlugin=56d5e249a98895a9797bebb9;
scope=card; idModel=58263201749710ed3c706bef;
value={"fields":{"ZIn76ljn-4yeYvz":2,"ZIn76ljn-c2yhZH":1}};
access=shared}
#{id=5834536fcff0525f26f9e53b; idPlugin=56d5e249a98895a9797bebb9;
scope=card; idModel=567031ea6a01f722978b795d;
value={"fields":{"ZIn76ljn-4yeYvz":4,"ZIn76ljn-c2yhZH":3}};
access=shared}
The fields collection is basically key/pair. The random characters correspond to the property and the value after that is what was set on the custom property. In this case it is an 'index' for the value in the dropdown. These two fields have a 'priority' (low, medium, high) and a 'classification' (Bug, Change Request, etc) for us. (We are using labels for something else).
So you'll have to create another fucntion where you can parse this data out. I am sure there are better ways to do it -- but this is what I have now:
function Get-TrelloCustomPropertyData($propertyData)
{
$data = $propertyData.Replace('{"fields":{', '')
$data = $data.Replace('}}', '')
$data = $data.Replace('"', '')
$sepone = ","
$septwo = ":"
$options = [System.StringSplitOptions]::RemoveEmptyEntries
$obj = $data.Split($sepone, $options)
$cardCustomFields = Get-TrelloCustomFieldObject
foreach($pair in $obj)
{
$field = $pair.Split($septwo,$options)
if (-Not [string]::IsNullOrWhiteSpace($field[0].Trim()))
{
switch($field[0].Trim())
{
'ZIn76ljn-4yeYvz' {
switch($field[1].Trim())
{
'1'{
$cardCustomFields.Priority = "Critical"
}
'2'{
$cardCustomFields.Priority = "High"
}
'3'{
$cardCustomFields.Priority = "Medium"
}
'4'{
$cardCustomFields.Priority = "Low"
}
}
}
'ZIn76ljn-c2yhZH' {
switch($field[1].Trim())
{
'1'{
$cardCustomFields.Classification = "Bug"
}
'2'{
$cardCustomFields.Classification = "Change Request"
}
'3'{
$cardCustomFields.Classification = "New Development"
}
}
}
'ZIn76ljn-uJyxzA'{$cardCustomFields.Estimated = $field[1].Trim()}
'ZIn76ljn-AwYurD'{$cardCustomFields.Actual = $field[1].Trim()}
}
}
}
return $cardCustomFields
}
Get-TrelloCustomFieldObject is another ps function that I set up to build an object based on the properties I know that I have defined.
function Get-TrelloCustomFieldObject
{
[CmdletBinding()]
param()
begin
{
$ErrorActionPreference = 'Stop'
}
process
{
$ccf = New-Object System.Object
$ccf | Add-Member -type NoteProperty -name Priority -value "None"
$ccf | Add-Member -type NoteProperty -name Classification -value "None"
$ccf | Add-Member -type NoteProperty -name Estimated -value ""
$ccf | Add-Member -type NoteProperty -name Actual -value ""
return $ccf
}
}

Why is TFS Rest API for fetching the TFVC changesets returning only 256 items?

Why is the TFS Rest API for fetching the TFVC changesets returning only 256 items. I tried using the $Top to get more than 256 results. But no use.
The API for getting Git repository commits is working fine.
I am referring to https://www.visualstudio.com/en-us/docs/integrate/api/tfvc/changesets
Sample request
https://tfs.domain.com/tfs/defaultcollection/projectname/_apis/tfvc/changesets?$searchCriteria.fromDate=2016-07-12T17:49:01&$skip=0&$top=500
I tried without $skip and $searchcriteria.fromdate also. Please help me understand how to get all the results.
I managed to resolve this strange logic (thank you MSFT guys you rock :/).
In my case I want to get all changesets.
1) you need to get first 256 changesets (I used orderby in case of different order in responses):
<projectName>/_apis/tfvc/changesets?$top=256&orderby=id desc&searchCriteria.itemPath=<your_path>&api-version=1.0
2) if you have more then 0 items in response get last value of changesetNumber from response. If 0 - you got all changesets.
3) get next 256 items starting from changesetNumber:
<projectName>/_apis/tfvc/changesets?searchCriteria.toId=<changesetNumber>&$top=256&orderby=id desc&searchCriteria.itemPath=<your_path>&api-version=1.0
You need to skip first changeset (you already have this value) in response.
4) go to Step 2
So you need to replace changesetId with createdDate and searchCriteria.toId with searchCriteria.fromDate.
I hope my solution will help you.
P.S. I was unable to find any feedback on connect website.
Based on #Vitaly comments above, I've implemented the solution in posh here. ALso extracted the script below:
function Get-MaxChangeset($Project, $LastChngset)
{
$projectUri = "$rootTfsUri/$($Project.name)/_apis/tfvc/changesets?api-version=3.1&`$top=256&orderby=id desc"
$lastchangeset = $LastChngset
if($lastchangeset -ne $null){
$projectUri += "&searchCriteria.toId=$($lastchangeset.changesetId)"
}
$nestedsubresponse = Invoke-WebRequest -Uri $projectUri -UseDefaultCredentials -Method Get -Verbose -UseBasicParsing
$nestedsubresponseObject = $nestedsubresponse.Content | ConvertFrom-Json
if($nestedsubresponseObject.count -ge 1)
{
# exclude false positives.
$lastchangeset = $nestedsubresponseObject.value | `
where-object {
($_.checkedInBy.displayName -notlike "*Project Collection Service Accounts*") } | `
Sort-Object changesetId -Descending | Select-Object -First 1
if( ($lastchangeset -eq $null) -and ($nestedsubresponseObject.count -ge 256) )
{
# More records to search. Pick the bottom from current list and send it back for next batch api call.
$lastchangeset = $nestedsubresponseObject.value | Sort-Object changesetId | Select-Object -First 1
$lastchangeset = Get-MaxChangeset $Project $lastchangeset
}
}
return $lastchangeset
}
$rootTfsUri = "http://tfs:8080/tfs/DefaultCollection"
$allProjectsUri = "$rootTfsUri/_apis/projects?api-version=3.1&`$top=256"
$projectStats = #{}
$response = Invoke-WebRequest -Uri $allProjectsUri -UseDefaultCredentials -Method Get -Verbose -UseBasicParsing
$responseObject = $response.Content | ConvertFrom-Json
if($responseObject.count -ge 1)
{
foreach($prj in $responseObject.value)
{
$lastchangeset = Get-MaxChangeset $prj $null
if($lastchangeset -ne $null)
{
$projectStats.Add("$($prj.name)", $lastchangeset.createdDate)
}
else
{
$projectStats.Add("$($prj.name)", $lastchangeset)
}
}
$projectStats.GetEnumerator() | Export-Csv "CheckInHistory.csv"
}
HTH,
Sam

Safe late variable expansion in Powershell

I found a very old thread here that seemed to answer the question, but when I tried to implement the code I am not getting the expected variable expansion. Based on this
$Arguments = '$foo (Notepad.exe) bar'
$foo = 'Foooooo'
$InitSB = {$ExecutionContext.SessionState.Applications.Clear(); $ExecutionContext.SessionState.Scripts.Clear(); Get-Command | %{$_.Visibility = 'Private'}}
$SafeStringEvalSB = {param($str) $str}
$job = Start-Job -Init $InitSB -ScriptBlock $SafeStringEvalSB -ArgumentList $Arguments
Wait-Job $job > $null
Receive-Job $job
I would expect to get back Foooooo (Notepad.exe) bar
What have I got wrong here? And is there any difference between v2 and later versions?
I do have this working using $ExecutionContext.InvokeCommand.ExpandString(), but that allows for arbitrary code to execute as well, which I am trying to avoid. Ultimately I have a large number of different strings stored in XML, with different permutations of variables interspersed in the strings, and the actual values of those different variables are assigned at run time. And I need to push those values into the specific string of many possible wherever they occur. Because there are so many potential strings and variables I can't easily use a simple format approach. So ultimately I need the functionality of $ExecutionContext.InvokeCommand.ExpandString() but limited to actually just expanding variables, with no other code execution.
Any help greatly appreciated.
Alternatively you could just replace the text.
Param($var1,$var2,$var3)
$Content = Get-Content C:\Path\To\file.xml
$Content -replace '\$var1', "$var1" -replace '\$var2', "$var2" -replace '\$var3', "$var3"
As you have it written you won't get what you expect purely because you have single quotes around your string that you are assigning to $Arguments, meaning $foo wouldn't be treated as a variable anyway.
EDIT: Still won't work even with double quotes. Must assign value to $foo before referencing it in $Arguments.
$foo = 'Foooooo'
$Arguments = "$foo (Notepad.exe) bar"
$InitSB = {$ExecutionContext.SessionState.Applications.Clear(); $ExecutionContext.SessionState.Scripts.Clear(); Get-Command | %{$_.Visibility = 'Private'}}
$SafeStringEvalSB = {param($str) $str}
$job = Start-Job -Init $InitSB -ScriptBlock $SafeStringEvalSB -ArgumentList $Arguments
Wait-Job $job > $null
Receive-Job $job

PowerShell Script DB Query not passing all results

Good morning stackoverflow. I have a PowerShell script that is executing a SQL query against an Oracle database and then taking the results and passing them to a local shell command. It works, mostly. What is happening is some of the results are being dropped and the only significance I can see about these is that they have a couple of columns that have null values (but only 2 out of the 8 columns that are being returned). When the query is executed in sQL developer I get all expected results. This issue applies to the $eventcheck switch, the $statuscheck works fine. The Powershell script is below:
param(
[parameter(mandatory=$True)]$username,
[parameter(mandatory=$True)]$password,
$paramreport,
$paramsite,
[switch]$eventcheck,
[switch]$statuscheck
)
$qry1 = Get-Content .\vantageplus_processing.sql
$qry2 = #"
select max(TO_CHAR(VP_ACTUAL_RPT_DETAILS.ETLLOADER_OUT,'YYYYMMDDHH24MISS')) Completed
from MONITOR.VP_ACTUAL_RPT_DETAILS
where VP_ACTUAL_RPT_DETAILS.REPORTNUMBER = '$($paramreport)' and VP_ACTUAL_RPT_DETAILS.SITE_NAME = '$($paramsite)'
order by completed desc
"#
$connString = #"
Provider=OraOLEDB.Oracle;Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST="HOST")(PORT="1521"))
(CONNECT_DATA=(SERVICE_NAME="SERVICE")));User ID="$username";Password="$password"
"#
function Get-OLEDBData ($connectstring, $sql) {
$OLEDBConn = New-Object System.Data.OleDb.OleDbConnection($connectstring)
$OLEDBConn.open()
$readcmd = New-Object system.Data.OleDb.OleDbCommand($sql,$OLEDBConn)
$readcmd.CommandTimeout = '300'
$da = New-Object system.Data.OleDb.OleDbDataAdapter($readcmd)
$dt = New-Object System.Data.DataTable
[void]$da.fill($dt)
$OLEDBConn.close()
return $dt
}
if ($eventcheck)
{
$output = Get-OLEDBData $connString $qry1
ForEach ($lines in $output)
{
start-process -NoNewWindow -FilePath msend.exe -ArgumentList #"
-n bem_snmp01 -r CRITICAL -a CSG_VANTAGE_PLUS -m "The report $($lines.RPT) for site $($lines.SITE) has not been loaded by $($lines.EXPDTE)" -b "vp_reportnumber='$($lines.RPT)'; vp_sitename='$($lines.SITE)'; vp_expectedcomplete='$($lines.SIMEXPECTED)'; csg_environment='Production';"
"# # KEEP THIS TERMINATOR AT THE BEGINNING OF LINE
}
}
if ($statuscheck)
{
$output = Get-OLEDBData $connString $qry2
# $output | foreach {$_.completed}
write-host -nonewline $output.completed
}
So that you can see the data, below is a csv output from Oracle SQL Developer with the ACTUAL results to the query that is being referenced by my script. Of these results lines 4, 5, 6, 7, 8, 10 are the only ones being passed along in the ForEach loop, while the others are not even captured in the $output array. If anyone can advise of a method for getting all of the results passed along, I would appreciate it.
"SITE","RPT","LSDTE","EXPDTE","CIMEXPECTED","EXPECTED_FREQUENCY","DATE_TIMING","ETME"
"chrcse","CPHM-054","","2014/09/21 12:00:00","20140921120000","MONTHLY","1",
"chrcse","CPSM-226","","2014/09/21 12:00:00","20140921120000","MONTHLY","1",
"dsh","CPSD-176","2014/09/28 23:20:04","2014/09/30 04:00:00","20140930040000","DAILY","1",1.41637731481481481481481481481481481481
"dsh","CPSD-178","2014/09/28 23:20:11","2014/09/30 04:00:00","20140930040000","DAILY","1",1.4162962962962962962962962962962962963
"exp","CPSM-610","2014/08/22 06:42:10","2014/09/21 09:00:00","20140921090000","MONTHLY","1",39.10936342592592592592592592592592592593
"mdc","CPKD-264","2014/09/24 00:44:32","2014/09/30 04:00:00","20140930040000","DAILY","1",6.35771990740740740740740740740740740741
"nea","CPKD-264","2014/09/24 01:00:31","2014/09/30 03:00:00","20140930030000","DAILY","1",6.34662037037037037037037037037037037037
"twtla","CPOD-034","","2014/09/29 23:00:00","20140929230000","DAILY","0",
"twtla","CPPE-002","2014/09/29 02:40:35","2014/09/30 06:00:00","20140930060000","DAILY","1",1.27712962962962962962962962962962962963
"twtla","CPXX-004","","2014/09/29 23:00:00","20140929230000","DAILY","0",
It appears, actually, that somehow a comment that was in the query was causing this issue. I removed it and the results started returning normal. I have no idea why this would be the case, unless it has to do with the way the import works (does it import everything as one line?). Either way, the results are normal now.

trying to figure out how to call two variables in an array in powershell

just starting to learn loops and arrays. i understand how to call a single variable in an array ie:
$animals = gc "c:\temp\animals.txt"
foreach ($animal in $animals)
{write-host "The"$animal "sleeps tonight."}
what i'm trying to figure out is how to call two different variables from two different arrays...ie:
$animals = gc "c:\temp\animals.txt"
$colors = gc "c:\temp\colors.txt"
this is the part where I'm confused. how do I call a foreach loop to cycle though both files simultaneously?
desired output: The white lion sleeps tonight, The black panther sleeps tonight, etc...
One way is to use arry indexing. Assuming both files have same line count:
$animals = gc c:\temp\animals.txt
$colors = gc c:\temp\colors.txt
for($i=0; $i -lt $animals.length; $i++)
{
#print first line from animals
$animals[$i]
#print first line from colors
$colors[$i]
}
Assuming you have two text files (with same no. of entries) in C:\ you can write something like this -
$c = 0
$animal = Get-Content C:\Animal.txt
Get-Content C:\Color.txt | Foreach-Object{
Write-Host "The $_ $($animal[$c]) sleeps at night"
$c++
}