How to avoid errors when casting null REST data to datetime - rest

I am using a REST API to pull invoice data, from an external company, into an Excel file using PowerShell. I can pull the data just fine using the Invoke-RestMethod. Although, when it pulls the datetime fields, it is in a format I do not prefer. To automate my job as much as possible, is there a way to cast the data to datetime. Keep in mind, not every field has data so there are null fields which cause the errors. I basically want to pull all data, if it has an ugly-formatted datetime, change it to the way I want to see it, or if it is empty, just continue on.
I have tried [nullable[datetime]] (found it on google somewhere), but that throws other errors. I have tried using an 'IF' but that also generates in out set of syntax errors.
$out = Invoke-RestMethod -Uri $assetsURL
#$out.Invoices | Export-Excel $outputcsv
$out.Invoices |
Select-Object * |
ForEach-Object {
$Properties = [ordered]#{
InvoiceNumber = $_.InvoiceNumber
DueDate = $_.DueDate #[datetime]::parseexact($_.DueDate, 'yyyy-MM-ddThh:mm:ss', $null).ToString('yyyy-MM-dd')
Currency = $_.Currency
TotalDue = $_.TotalDue
PeriodFrom = $_.PeriodFrom #[datetime]::parseexact($_.PeriodFrom,'yyyy-MM-ddThh:mm:ss', $null).ToString('yyyy-MM-dd')
PeriodTo = $_.PeriodTo #[datetime]::parseexact($_.PeriodTo,'yyyy-MM-ddThh:mm:ss', $null).ToString('yyyy-MM-dd')
BillingName = $_.BillingName
BillingAddress = $_.BillingAddress
BillingAttentionTo = $_.BillingAttentionTo
BillingCity = $_.BillingCity
BillingState = $_.BillingState
BillingPostalCode = $_.BillingPostalCode
RemitToName = $_.RemitToName
RemitToAddress = $_.RemitToAddress
RemitToAttentionTo = $_.RemitToAttentionTo
RemitToCity = $_.RemitToCity
RemitToState = $_.RemitToState
RemitToPostalCode = $_.RemitToPostalCode
Lease = $_.Lease
Schedule = $_.Schedule
CreatedDate = $_.CreatedDate #[datetime]::parseexact($_.CreatedDate, 'yyyy-MM-ddThh:mm:ss', $null).ToString('yyyy-MM-dd')
}
New-Object psobject -Property $Properties
} | Export-Excel $outputcsv
Just updating because I may not have been clear on what the issue is. In the columns 'DueDate', 'PeriodFrom', 'PeriodTo', and 'CreatedDate', these are all pulled from the REST API. 'DueDate' and 'CreatedDate' seem to be properly populated but 'PeriodFrom' and 'PeriodTo' may or may not have a date.
Currently, without any trickery, the date (if it exists) returns as:
2019-09-05T00:00:00
I would like to return an empty field if it does not exist or return the date in the like the following if it does exit:
2019-09-05

You can use TryParseExact:
ForEach-Object {
$parsedDate = [datetime]::MinValue
$Properties = [ordered]#{ # remove the CreatedDate from the HashTable
[...]
if ([datetime]::TryParseExact($_.CreatedDate,'yyyy-MM-ddThh:mm:ss',
[System.Globalization.CultureInfo]::InvariantCulture,
[System.Globalization.DateTimeStyles]::None,[ref]$ParsedDate))
{$Properties.Add("CreatedDate", $parsedDate.ToString("yyyy-MM-dd"))}
}
else {
$Properties.Add("CreatedDate",$null)
}
Note that you can add array of string formats instead of a single string, for example:
[String[]]$DateStrings = "M/dd/yyyy h:mmtt","M/d/yyyy h:mmtt","MM/d/yyyy h:mmtt"
Then it would be:
[datetime]::TryParseExact($_.CreatedDate,$DateStrings...)

Related

PowerShell - attempting to build a variable based on concatenated string + PSObject member values

Good day. I am working on an AD computer object creation script that has evolved. I have looked for some ways to dynamically pull data out of a storage object; this started with a hashtable, but then I realized I need multiple "columns" of values so that I could request a 2nd or 3rd value that is stored.
This led me to the creation of some PSObjects. I have a simple array as well.
$SiteCode_SHA = [PSCustomObject]#{
CountryCode = "CH"
SiteCode = "SHA"
ContainerCode = "CHA"
}
$SiteCode_IND = [PSCustomObject]#{
CountryCode = "IN"
SiteCode = "MOB"
ContainerCode = "IND"
}
[array]$ComputerTypeArray = "LT", "PC", "PR"
[string]$SiteCodeInput = (Read-Host -Prompt "Prompt #1 Input Options: SHA, IND")
$ProposedComputerName = '$SiteCode_' + $SiteCodeInput + '.CountryCode'
I am attempting to build a variable that contains the CountryCode from one of the $SideCode_XXX objects, plus the SiteCode from the same object, plus a value from the $ComputerTypeArray (which I am successfully retrieving through Read-Host.)
I have a three character variable that is being input through Read-Host that matches the suffix of the $SiteCode_XXX object. I cannot figure out how to dynamically use the object and its contained values after obtaining input from the user.
Concatenation of texts does not allow me to utilize the PSObject or its contained values, which I expected to be the failure point, but I cannot figure out the standard for getting around this. The goal is to set the variable $ProposedComputerName to "IN" if the $SiteCodeInput is "IND", or "CH" if the $SiteCodeInput is "SHA". (There are other components to the computer name that I am dealing with once this single problem is resolved.)
Thank you for your time.
Instead of using multiple variables, organize your data into a hashtable that use the site code as its key for each entry:
# define the data objects, store them in a simple array
$Sites = #(
[PSCustomObject]#{
CountryCode = "CH"
SiteCode = "SHA"
ContainerCode = "CHA"
}
[PSCustomObject]#{
CountryCode = "IN"
SiteCode = "MOB"
ContainerCode = "IND"
}
)
# now create a hashtable that maps each site code to the corresponding object
$SiteCodeMap = #{}
$Sites |ForEach-Object {
$SiteCodeMap[$_.SiteCode] = $_
}
# ... and now we're ready to take user input
$SiteCodeInput = (Read-Host -Prompt "Prompt #1 Input Options: SHA, IND")
# remember to test whether the input is valid
if($SiteCodeMap.ContainsKey($SiteCodeInput)){
# use valid site code to resolve the corresponding data object and grab the CountryCode value
$ProposedComputerName = $SiteCodeMap[$SiteCodeInput].CountryCode
} else {
Write-Warning "No site with site code '$SideCodeInput' exists!"
}

How to replace/update the Value of an Attribute in LDAP Directory using PowerShell?

The entries in our companys Non-AD LDAP Server look like this:
uid = e145871
sn = Smith
givenName = John
department = Research & Development
department = Human Resource
And so on...
I've developed a PowerShell script to add specific attributes and values which is working just fine. Now I need to replace specific values but the issue is the identical attribute name. (In this case it's "department")
My goal is to replace "Research & Development" with "Something Else". If I run the following script it gets replaced but Human Resource is deleted as well. Is it possible to replace only one value without touching/deleting the other?
$r = New-Object -TypeName System.DirectoryServices.Protocols.ModifyRequest
$r.DistinguishedName = "uid=e145871,ou=identities,ou=users,o=items,dc=company,dc=domain,dc=com"
$DirectoryRequest_value = New-Object "System.DirectoryServices.Protocols.DirectoryAttributeModification"
$DirectoryRequest_value.Name = "department"
$DirectoryRequest_value.Contains("Research & Development")
$DirectoryRequest_value.Operation = [System.DirectoryServices.Protocols.DirectoryAttributeOperation]::Replace
$DirectoryRequest_value.Add("SomethingElse")
$r.Modifications.Add($DirectoryRequest_value)
$result = $connection.SendRequest($r)
Thanks!
The LDAP Replace operation replaces (or overwrites) the entire value of the attribute, including any existing values that might exist as part of a multi-valued attribute.
From RFC4511 ยง4.6 - "Modify Operation":
- operation: Used to specify the type of modification being
performed. Each operation type acts on the following
modification. The values of this field have the following
semantics, respectively:
[...]
replace: replace all existing values of the modification
attribute with the new values listed, creating the attribute
if it did not already exist. A replace with no value will
delete the entire attribute if it exists, and it is ignored
if the attribute does not exist.
Instead, add two separate modifications to the request - one to add "SomethingElse" and one to remove "Research & Development":
$targetObject = 'uid=e145871,ou=identities,ou=users,o=items,dc=company,dc=domain,dc=com'
$attributeName = 'department'
$oldValue = 'Research & Development'
$newValue = 'SomethingElse'
$request = [System.DirectoryServices.Protocols.ModifyRequest]::new()
$request.DistinguishedName = $targetObject
# This modification will add the new value "SomethingElse"
$addNewDepartment = #{
Name = $attributeName
Operation = 'Add'
} -as [System.DirectoryServices.Protocols.DirectoryAttributeModification]
$addNewDepartment.Add($newValue) |Out-Null
$request.Modifications.Add($addNewDepartment) |Out-Null
# This modification will remove the old value "Research & Development"
$removeOldDepartment = #{
Name = $attributeName
Operation = 'Delete'
} -as [System.DirectoryServices.Protocols.DirectoryAttributeModification]
$removeOldDepartment.Add($oldValue) |Out-Null
$request.Modifications.Add($removeOldDepartment) |Out-Null
$result = $connection.SendRequest($request)

How to get creation date in sitecore with powershell

I wrote a script in order to replace the "$date" in release date of many Sitecore items with their creation date (created).
I have a problem to get this field from Sitecore.
I tried this:
$rootItem = Get-Item master:/content
$sourceTemplate = Get-Item "/sitecore/content/.../item 1"
foreach($field in $sourceTemplate.Fields) {
if (($field -ne $null) -And ($field -like '$date')) {
$sourceTemplate.Editing.BeginEdit()
$CreatedDate = .......
$field.Value = [sitecore.dateutil]::ToIsoDate($CreatedDate)
$sourceTemplate.Editing.EndEdit()
}
}
I also tried to get this field by ID but it doesn't work.
Does someone have an idea please?
Thank you
If you want to check Sitecore built-in fields, you need to call $sourceTemplate.Fields.ReadAll(); first.
You should compare value of the field with $date string, not the field itself.
Then just get the string which is stored in the __Created field instead of getting date and then formatting it back to ISO date string.
And the last thing - don't call Editing.BeginEdit() and Editing.EndEdit() mutliple times for the same item - Sitecore runs some havily operations when it's called so make sure you only call it once per every item which needs it.
$sourceTemplate = Get-Item "/sitecore/content/home/test"
$sourceTemplate.Fields.ReadAll();
$editing = $false
foreach($field in $sourceTemplate.Fields) {
if ($field.Value -eq '$date') {
if (!$editing) {
$editing = $true
$sourceTemplate.Editing.BeginEdit();
}
$field.Value = $sourceTemplate.Fields["__Created"].Value
}
}
if ($editing) {
$edited = $sourceTemplate.Editing.EndEdit();
}

Convert data type of property for all objects in an array

Say I have an array of objects:
$a = #(
#{ Name = "A"; Value = "2016-01-02" },
#{ Name = "B"; Value = "2016-01-03" },
#{ Name = "C"; Value = "2016-01-04" }
)
The Value property is currently a String. I want to convert the Value property of each object to a DateTime. I could accomplish this with a for loop, but I was wondering if there is a more direct way to do it.
Yes, calculated properties.
$a | select #{N='Name';E={$_.Name}}, #{N='Value';E={ [datetime]$_.Value }}
This will change data type of the first object Value to datetime.
$a[0].value = [datetime]::ParseExact($a[0].value,'yyyy-MM-dd',$null)
And the loop from here :
foreach ($Obj in $a)
{
$Obj.Value = [datetime]::ParseExact($Obj.value,'yyyy-MM-dd',$null)
}
The #TessellatingHeckler 's answer, which you accepted, is not correct for your question! It is only change the representation of the data in the object, but not converted it as you ask. Even if you save it as a new object (or overwrite the original) this will change the object itself. You can see the difference if you run flowing code:
$b=$a | select #{N='Name';E={$_.Name}}, #{N='Value';E={ [datetime]$_.Value }}
#check the original and converted object
$a|gm
$b|gm

Passing custom object array to a function

I'm trying to pass an array of custom objects to a function for further processing of these objects.
Here's the function where I create my custom object array:
Function GetNetworkAdapterList
{
# Get a list of available Adapters
$hnet = New-Object -ComObject HNetCfg.HNetShare
$netAdapters = #()
foreach ($i in $hnet.EnumEveryConnection)
{
$netconprop = $hnet.NetConnectionProps($i)
$inetconf = $hnet.INetSharingConfigurationForINetConnection($i)
$netAdapters += New-Object PsObject -Property #{
Index = $index
Guid = $netconprop.Guid
Name = $netconprop.Name
DeviceName = $netconprop.DeviceName
Status = $netconprop.Status
MediaType = $netconprop.MediaType
Characteristics = $netconprop.Characteristics
SharingEnabled = $inetconf.SharingEnabled
SharingConnectionType = $inetconf.SharingConnectionType
InternetFirewallEnabled = $inetconf.InternetFirewallEnabled
SharingConfigurationObject = $inetconf
}
$index++
}
return $netAdapters
}
Then in my main code I call above function like this:
$netAdapterList = GetNetworkAdapterList
The $netAdapterList returns the expected data, and I can do stuff like:
$netAdapterList | fl Name, DeviceName, Guid, SharingEnabled
So far so good.
Now I want to call a function passing in the $netAdapterList
I've created a dummy function like this:
Function ShowAdapters($netAdapterListParam)
{
$netAdapterListParam | fl Name, DeviceName, Guid, SharingEnabled
}
And when I invoke it like this:
ShowAdapters $netAdapterList
Nothing gets printed out.
I've tried changing the function's signature but still no luck:
Function ShowAdapters([Object[]]$netAdapterListParam)
Function ShowAdapters([Object]$netAdapterListParam)
Function ShowAdapters([PSObject[]]$netAdapterListParam)
Function ShowAdapters([array]$netAdapterListParam)
Anybody knows what I'm doing wrong? How can I get to my custom objects inside the function?
Thanks for your reply #Christian. Tried your steps, copy pasting bits and pieces into the shell and it indeed worked. However If i run the full .ps1 script is not printing out anything.
I've run the script in Powershell IDE setting breakpoints inside the ShowAdapters function, and $netAdapterListParam has indeed the expected custom objects array I'm passing in, so I've narrowed down the issue to be in the FL commandlet.
For some reason $netAdapterList | fl Name, DeviceName, Guid, SharingEnabled did not work for me, so I ended up using the following instead:
$formatted = $netAdapterListParam | fl Name, DeviceName, Guid, SharingEnabled | Out-String
Write-Host $formatted
That did the trick and the 4 properties were printed on the screen.
Lessons learned:
1) The Powershell IDE built into Win7 can be a very useful tool for debugging scripts
2) Format-List can be quirky when formatting custom objects, so Out-String was required.