Using Postman to create multiple Websites via API & CSV?
Hi,
I’m testing out creating websites (or resources) via the API. I have a standard Post working in Postman just fine. However, when I then try to do the same thing via a CSV file in the Runner section, I’m getting 401 Authorization errors.
When I look at the data that’s being sent, it looks the same as what’s sent when I run it manually.
Is there something special I need to do when running a Post command via the Runner vs the manual Send command?
Thanks.
Kel L
Posted 3 years ago·Last reply 2 years ago
40 comments
Kel L
OP2 years agoWell, just bumping this because it came up again yesterday. I was able to create a device group with no issues, but simply by change it from device/groups to website/groups made it all break. It ended up being the version. When I tried to add it to the URL it didn’t help. What seemed to work was to specify it in the header like this:
Once I did that, everything seemed to start working. Just in case it comes up for me again, I’m documenting it here. Or if it helps anyone else, there you go. :)
Steven Villardi
·3 years agoHey there, just wanted to throw out a mention for the Logic.Monitor PS module. It is a wrapper for the LM API and works to make working with the API easier for admins use to interfacing with powershell. There are around ~150 cmdlets with more being added each week. Feel free to check it out as it will make light work of what you are attempting to do without having to learn python.
GitHub:
https://github.com/stevevillardi/Logic.Monitor
PS Gallery:
https://www.powershellgallery.com/packages/Logic.Monitor/
https://community.logicmonitor.com/lm-tech-talk-33/logic-monitor-powershell-module-3562Intro post with some quick start info:
Amanda Cook
·3 years agoHey there,
I am by no means the most organized PowerShell person, but I have successfully gotten this working by reading a csv file for my website names. Additionally I have put the API credential and header work into a function and I call that when you see send-request.
CSV Example
google.com
amazon.com
$websites = Import-Csv -Path "PUT YOUR FILE PATH HERE" header name
$resourcePath = "/website/websites/"
$queryParams = ""
# Loop through each website and create a new website with the given request body
$i=0
foreach ($website in $websites){
$data='{
"type": "webcheck",
"domain": "' + $websites[$i].name + '",
"testLocation": {
"smgIds": [2, 3, 4]
},
"name": "' + $websites[$i].name + '",
"steps": [
{
"url": "' + $websites[$i].name + '",
"timeout": 120
}
]
}'
#Main Client Group
#$data='{"type":"webcheck","domain":"' + $websites[$i].name + '","testLocation":{"smgIds":[2, 3, 4]},"name":"' + $websites[$i].name + '","steps":[{"url":"' + $websites[$i].name + '","timeout":120}]}'
$results = Send-Request $resourcePath $httpVerb $queryParams $data
write-host $data
Write-Host $results
$i = $i + 1
}
Also, for website groups I use this:
$CsvFilePath = "C:\Temp\SiteCodes.txt"
# Read the CSV file
$SiteCodes = Import-Csv -Path $CsvFilePath -header siteCode
$resourcePath = '/dashboard/groups/'
# Loop through the site codes and create dashboard groups
foreach ($SiteCode in $SiteCodes) {
# Build the JSON request body
$data = @{
name = "$($SiteCode.siteCode) Dashboard Group"
} | ConvertTo-Json
# Send the API request to create the dashboard group
$Response = send-request -resourcePath $resourcePath -httpVerb POST -data $data
# Print the response to the console
Write-Output "Dashboard group created: $($Response.name)"
}
Maybe this helps and maybe not but figured I would share what worked for me.
kendall
·3 years agoimport json
response_dict = json.loads(response_string)
This code you shared above will not work. parsed = json.loads(response.content) will work, but not the way you previously wrote it... I illustrated further so it wasn’t as confusing for someone just getting started.
LM User
·3 years agoparsed = json.loads(response.content) works just as well. No need to set it as a variable if you’re only going to reference it once.
kendall
·3 years agoTo add to Stuart’s advice @Kelemvor , you will also need to add something similar:
strdata = response.content
parsed = json.loads(strdata)
If you don’t add the bolded line above, json.loads() will not work properly.
LM User
·3 years agojson.loads(x) takes a string (x) and converts it from json format into a dictionary. json load string.
json.dumps(y) takes a dictionary (y) and converts it from a dictionary into a string in json format. json dump string.
LM User
·3 years agoConverts from the json string to a python dictionary (equivalent to posh hashtable)
LM User
·3 years agoimport json
response_dict = json.loads(response_string)
Kel L
OP3 years agoOK. I decided to start over. I installed VSCode and have Python working. I’m able to do GETs now, but the data is coming back in a format with a ton of \n’s in it as text. Do I need to do something to get the results with actual line returns so it’s readable?
Thanks for all the assistance so far.
E.g.
kendall
·3 years agoThat’s wrong. Query parameters and data are two different things. You can have query parameters in a POST. The difference is that for a GET, LM’s API doesn’t require any payload, which is why the data variable is set to an empty string in the GET script.
I was referring to how to use LM API, not in general, so my assertion stands and the code works.
LM User
·3 years agoThat’s wrong. Query parameters and data are two different things. You can have query parameters in a POST. The difference is that for a GET, LM’s API doesn’t require any payload, which is why the data variable is set to an empty string in the GET script.
kendall
·3 years ago@Kelemvor ~ check the differences noted in these screenshots:
So basically, with a get, the query parameters are sent by appending to the url (first screenshot). With a post, the data is sent with the request (second screenshot).
LM User
·3 years agoBecause the GET does not need the payload, so your signature also shouldn’t include the payload.
Kel L
OP3 years agoWell, I’m not sure what changed, but I was able to get the POST to work and create a website. However, why would I have success POSTing to a website, but then get an Authentication Error when I change it to a GET to grab website Groups, without changing anything else?
Argh!
I can do a GET just fine with resource groups, alerts, etc, but as soon as I try to do website groups It fails. Is there something special about that one?
kendall
·3 years agoI disagree…
And I respect your opinion. However, some customers just want to see how the api works or test basic functionality. Also, the requests library is widely used and still actively developed. It’s just an easier way to get started.
LM User
·3 years agoI disagree that people new to Python should first learn the Requests method of interacting with LM’s API. That’s like making people learn how to rebuild an internal combustion engine before being allowed to buy an electric car.
Use the SDK; it’s object oriented and much easier to get started with, especially for new Python programmers.
I had a similar experience when learning Python. I was trying to learn how to code robots for the Raspberry Pi. I took all their examples, learned all the long methods and studied how those methods perfectly aligned with the physical hardware in a very bare metal way. It was hard, but I was able to do what I needed to do. Then the gpiozero library came out, which is an SDK for the bare metal GPIO calls. Some of my code blocks went from 50-70 lines to two (2). Every time I built something new using gpiozero, I was astounded at how much more simple things were. I didn’t have to create a pulldown resistor object, then instantiate another object with that object and the pin object just so i could call it. I could just create an LED and pass it the pin number and voila, I had a working LED.
Just because the Requests method of interacting with the API exists doesn’t mean it’s the right way for everyone or even most people. Most people who are using LM aren’t full time developers. They’re infrastructure admins who don’t necessarily want to shift to being developers. They’re part time developers for whom the SDK is a great tool.
Consider the example I showed earlier, which is a complete working example of adding a website to LM:
This makes use of my lmwrapper library to simplify all your API interactions. The `from lm import lm` line imports the sdk, sets up the object, passes in your credentials (from a file you have stored outside the script because you should never put your credentials in the script), and otherwise gets everything ready to go. Body creates the definition you want to add and the lm.add_website() method adds it. This is the entirety of the script needed to add a website using the SDK.
Compare this to any script (even any of mine) that use the request library to interact with the API directly. Not only is the code longer, but there are steps in there that aren’t always understood and should never be coded more than once (that’s what imports are for).
Shorter code through the SDK is easier to read and understand what is going on.
kendall
·3 years agoI’d take a Python shell. I guess I could just use the one from the LM example page, but those are old.
All I’d have to do is change the APIKey stuff and then sub in my “Data” information, right? One of the main reasons I was learning Python is for API stuff, but I’m just going through an online course so I don’t know when we’ll get to that part. ;)
You can, of course, take a shell from our support docs, but unfortunately our docs haven’t quite kept pace with the development of our API, and most of those scripts reflect v1 or v2. The script from the repo I linked yesterday (https://github.com/krshearman/LMAPI_v3/blob/main/PostAddWebsite.py) has been recently tested and is working. All you’ll need to do is add your company name, creds, and your own data.
Hope it helps@Kelemvor !
LM User
·3 years agoYeah, it should work. If it doesn’t maybe they’ll finally update them and/or start showing examples using the SDK. Make sure to add the header to force it to v3 of the API:
Suggest you not use the example destination. That website hasn’t existed for years (at least 4 that I know of).
Kel L
OP3 years agoI’d take a Python shell. I guess I could just use the one from the LM example page, but those are old.
All I’d have to do is change the APIKey stuff and then sub in my “Data” information, right? One of the main reasons I was learning Python is for API stuff, but I’m just going through an online course so I don’t know when we’ll get to that part. ;)
kendall
·3 years agoOK, adding ?v=2 or 3 to the resourcepath now gives me an Authentication error. 1401. Making progress I guess.
To pass the version header, add this @Kelemvor :
$headers.Add("X-Version",'3')Just under the other lines that are similar.
Otherwise, I have a working python script to add a website here: https://github.com/krshearman/LMAPI_v3/blob/main/PostAddWebsite.py
LM User
·3 years agoI’m pushing Python so I don’t have to learn Powershell just to be able to help you. ;-)
LM User
·3 years agoLearn how to do API calls in PS vs. learn how to do Python. 6’s really. I get it though. I’m having to learn two new tools and mongodb. I can’t count the number of times I’ve said, “see, if it just used jinja2 templating, i’d be done by now”.
Kel L
OP3 years agoSee, I don’t know Python. I’m working on learning it but still a beginner. That’s why I got excited when Powershell was mentioned since I’ve been using that for years, just never for API calls. No luck with adding it to the header. Still poking around.
Thanks!
LM User
·3 years agoTry switching it to the header. That way there’s no way it can be involved in the signature calculation.
Also, switch to Python and use the SDK:
Kel L
OP3 years agoOK, adding ?v=2 or 3 to the resourcepath now gives me an Authentication error. 1401. Making progress I guess.
LM User
·3 years agoCan’t remember if it should be a string or not. Not a POSH guy anyway, so i don’t know if it matters even.
LM User
·3 years agoWe posted at the same time. I think the problem is that you’re not forcing it to use v3.
LM User
·3 years agoThis is in the response from the server. Instead of returning json, it’s returning HTML. That shook something loose: Are you setting it to use v3 of the API? Either add a query parameter `v=3` or a header X-Version : 3. If you’re using v3 of the API, it should be returning json no matter what the error is. The fact that you’re getting HTML back might mean you’re hitting v2 or (eek) v1 of the API.
Kel L
OP3 years agoHere’s the whole script in case anything jumps out at you.
LM User
·3 years agoOk, are you converting your payload to json before sending it?
Kel L
OP3 years agoYeah, that’s what I found online, but the Headers section in the script seems to be setting that.
<# Construct Headers #>
$auth = 'LMv1 ' + $accessId + ':' + $signature + ':' + $epoch
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization",$auth)
$headers.Add("Content-Type",'application/json')
LM User
·3 years agoYou need to set the Content-Type header to application/json. Your request probably didn’t specify the type of content it was passing in so the API doesn’t know how to parse your payload; the api doesn’t assume your payload is json.
Kel L
OP3 years agoAny idea what this error means? Just trying to create a website via the API. I’m using the same “Data” as I use in Postman and it works fine there, but not from Powershell. I’m not sure where it’s getting all the CSS stuff from as it’s not in the code anywhere. I even copied and pasted into Notepad first to try to remove any formatting.
Invoke-RestMethod : HTTP Status 415 – Unsupported Media Typebody {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}HTTP Status 415 – Unsupported
Media TypeType Status ReportMessage Unsupported Media TypeDescription The origin server is refusing to service the request because the payload is in a format not supported by this method on the target resource.LogicMonitor Technical Operations
At line:52 char:13
+ $response = Invoke-RestMethod -Uri $url -Method $httpVerb -Body $data ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
Status:200
Joe Williams
·3 years agoThe examples on that page and mine above will work with V3, the returned data structure is just different.
Kel L
OP3 years agoAh thanks. I’ll take a look. I also found this page, but it says it’s from v1. Hopefully it still works too.
https://www.logicmonitor.com/support/rest-api-developers-guide/v1/rest-api-v1-examples
Mike Moniz
·3 years agoI’m not familiar with Postman at all myself but one thing about LM API calls is that you need to include any POST/PUT data into the sha256 algorithm that is used in the LMv1 auth header or it will fail. So need to make sure Postman is doing that when using the “Runner” section you talked about.
For PowerShell, there are some unofficial modules in PSGallery otherwise LM provides some examples at https://www.logicmonitor.com/support/rest-api-developers-guide/v1/rest-api-v1-examples#h-powershell-examples, just add the X-Version header to use APIv3 (v1 and v2 are going to be depreciated soon)
Joe Williams
·3 years agoThis is dirty but should get the idea out there. This was a tab delimeted file not a csv but reading a csv is pretty simple.
Kel L
OP3 years agoI have no idea. I don’t know Postman or Python very well, but I do know Powershell. Although I’ve never used it for doing any API stuff. Do you have a sample of using Powershell do hit the LM APIs by chance that I could use as a starting point?
Thanks.
Joe Williams
·3 years agoIs the pre-request script running each time you are making the call?
Personally this is something I would use Python or Powershell for. Loop through the items in the csv then call the API or SDK with that.