I am trying to figure out how to get my powershell script to work but keep getting a 404 error. The PS script shows a form to fill out information I have the data saved in a txt file on the workstation and I want to update the data for the computer in meraki. What is wrong and how could I fix this. Is what I am trying to do possible? I want to update the notes and or tags on a computer.

Error
Failed to update device: The remote server returned an error: (404) Not Found.
Organization ID: Redacted
Network ID: Redacted
URL: https://api.meraki.com/api/v1/devices/MZ00AVWF
Headers: {
"X-Cisco-Meraki-API-Key": "Redacted",
"Content-Type": "application/json"
}
Data being pushed to the API:
{
"serial": "MZ00AVWF",
"tags": [
"IS",
"Imprivata_Type_1"
],
"name": "ISWS09",
"notes": "Phone Extension: 5166\nDoor Tag: B-105\nOwner: Redacted@Redacted.org"
}
PowerShell Script
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
# Define the API key and organization ID
$apiKey = 'Redacted'
$orgId = 'Redacted' # Replace with your actual organization ID
# Function to get the serial number of the computer
function Get-ComputerSerialNumber {
$wmi = Get-WmiObject -Class Win32_BIOS
return $wmi.SerialNumber
}
# Function to get the system name of the computer
function Get-ComputerName {
return $env:COMPUTERNAME
}
# Function to get AD users with sonomavalleyhospital.org email, excluding disabled accounts
function Get-ADUsersWithEmail {
Import-Module ActiveDirectory
try {
$users = Get-ADUser -Filter {EmailAddress -like '*@sonomavalleyhospital.org' -and Enabled -eq $true} -Properties EmailAddress, DisplayName | Where-Object { $_.EmailAddress -ne $null }
return $users
} catch {
Write-Error "Failed to fetch AD users: $_"
return @()
}
}
# Function to get all networks in the organization
function Get-MerakiNetworks {
$url = "https://api.meraki.com/api/v1/organizations/$orgId/networks"
$headers = @{
"X-Cisco-Meraki-API-Key" = $apiKey
}
try {
$response = Invoke-RestMethod -Method Get -Uri $url -Headers $headers
return $response
} catch {
Write-Error "Failed to fetch networks: $_"
return @()
}
}
# Function to read data from the file
function Read-SystemInfo {
$filePath = 'C:/Windows/OEM/System_INFO.txt'
if (Test-Path $filePath) {
$data = Get-Content $filePath | Out-String
return $data
}
return $null
}
# Function to write data to the file
function Write-SystemInfo {
param (
[string]$data
)
$filePath = 'C:/Windows/OEM/System_INFO.txt'
if (Test-Path $filePath) {
$backupPath = "C:/Windows/OEM/System_INFO_$(Get-Date -Format 'yyyyMMddHHmmss').bak"
Copy-Item $filePath -Destination $backupPath
}
$data | Out-File -FilePath $filePath
}
# Get AD users and Meraki networks
$adUsers = Get-ADUsersWithEmail
$merakiNetworks = Get-MerakiNetworks
# Read existing data from the file
$existingData = Read-SystemInfo
$existingDataHash = @{}
if ($existingData) {
$existingData -split "`n" | ForEach-Object {
$parts = $_ -split ": "
if ($parts.Length -eq 2) {
$existingDataHash[$parts[0].Trim()] = $parts[1].Trim()
}
}
}
# Get the system name and serial number
$systemName = Get-ComputerName
$serialNumber = Get-ComputerSerialNumber
# Create the form
$form = New-Object System.Windows.Forms.Form
$form.Text = "Device Information"
$form.Size = New-Object System.Drawing.Size(400, 700)
$form.StartPosition = "CenterScreen"
# Display system name and serial number
$systemNameLabel = New-Object System.Windows.Forms.Label
$systemNameLabel.Text = "System Name: $systemName"
$systemNameLabel.Location = New-Object System.Drawing.Point(10, 20)
$systemNameLabel.AutoSize = $true
$systemNameLabel.MaximumSize = New-Object System.Drawing.Size(380, 0)
$form.Controls.Add($systemNameLabel)
$serialNumberLabel = New-Object System.Windows.Forms.Label
$serialNumberLabel.Text = "Serial Number: $serialNumber"
$serialNumberLabel.Location = New-Object System.Drawing.Point(10, 50)
$serialNumberLabel.AutoSize = $true
$serialNumberLabel.MaximumSize = New-Object System.Drawing.Size(380, 0)
$form.Controls.Add($serialNumberLabel)
# Create dropdown for network selection
$networkLabel = New-Object System.Windows.Forms.Label
$networkLabel.Text = "Select Network:"
$networkLabel.Location = New-Object System.Drawing.Point(10, 80)
$networkLabel.AutoSize = $true
$networkLabel.MaximumSize = New-Object System.Drawing.Size(380, 0)
$form.Controls.Add($networkLabel)
$networkComboBox = New-Object System.Windows.Forms.ComboBox
$networkComboBox.Location = New-Object System.Drawing.Point(150, 80)
foreach ($network in $merakiNetworks) {
$networkComboBox.Items.Add($network.name)
}
$form.Controls.Add($networkComboBox)
# Create dropdown for AD user with auto-filter
$userLabel = New-Object System.Windows.Forms.Label
$userLabel.Text = "Assign to AD User:"
$userLabel.Location = New-Object System.Drawing.Point(10, 120)
$userLabel.AutoSize = $true
$userLabel.MaximumSize = New-Object System.Drawing.Size(380, 0)
$form.Controls.Add($userLabel)
$userComboBox = New-Object System.Windows.Forms.ComboBox
$userComboBox.Location = New-Object System.Drawing.Point(150, 120)
$userComboBox.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDown
$userComboBox.AutoCompleteMode = [System.Windows.Forms.AutoCompleteMode]::SuggestAppend
$userComboBox.AutoCompleteSource = [System.Windows.Forms.AutoCompleteSource]::ListItems
foreach ($user in $adUsers) {
$userComboBox.Items.Add($user.DisplayName)
}
$form.Controls.Add($userComboBox)
# Create text fields for notes
$phoneLabel = New-Object System.Windows.Forms.Label
$phoneLabel.Text = "Phone Extension:"
$phoneLabel.Location = New-Object System.Drawing.Point(10, 160)
$phoneLabel.AutoSize = $true
$phoneLabel.MaximumSize = New-Object System.Drawing.Size(380, 0)
$form.Controls.Add($phoneLabel)
$phoneTextBox = New-Object System.Windows.Forms.TextBox
$phoneTextBox.Location = New-Object System.Drawing.Point(150, 160)
if ($existingDataHash.ContainsKey('Phone Extension')) {
$phoneTextBox.Text = $existingDataHash['Phone Extension']
}
$form.Controls.Add($phoneTextBox)
$departmentLabel = New-Object System.Windows.Forms.Label
$departmentLabel.Text = "Department:"
$departmentLabel.Location = New-Object System.Drawing.Point(10, 200)
$departmentLabel.AutoSize = $true
$departmentLabel.MaximumSize = New-Object System.Drawing.Size(380, 0)
$form.Controls.Add($departmentLabel)
$departmentTextBox = New-Object System.Windows.Forms.TextBox
$departmentTextBox.Location = New-Object System.Drawing.Point(150, 200)
if ($existingDataHash.ContainsKey('Department')) {
$departmentTextBox.Text = $existingDataHash['Department']
}
$form.Controls.Add($departmentTextBox)
$wowLabel = New-Object System.Windows.Forms.Label
$wowLabel.Text = "WOW:"
$wowLabel.Location = New-Object System.Drawing.Point(10, 240)
$wowLabel.AutoSize = $true
$wowLabel.MaximumSize = New-Object System.Drawing.Size(380, 0)
$form.Controls.Add($wowLabel)
$wowCheckBox = New-Object System.Windows.Forms.CheckBox
$wowCheckBox.Location = New-Object System.Drawing.Point(150, 240)
if ($existingDataHash.ContainsKey('WOW') -and $existingDataHash['WOW'] -eq 'Yes') {
$wowCheckBox.Checked = $true
}
$form.Controls.Add($wowCheckBox)
$locationLabel = New-Object System.Windows.Forms.Label
$locationLabel.Text = "Location:"
$locationLabel.Location = New-Object System.Drawing.Point(10, 280)
$locationLabel.AutoSize = $true
$locationLabel.MaximumSize = New-Object System.Drawing.Size(380, 0)
$form.Controls.Add($locationLabel)
$locationTextBox = New-Object System.Windows.Forms.TextBox
$locationTextBox.Location = New-Object System.Drawing.Point(150, 280)
if ($existingDataHash.ContainsKey('Location')) {
$locationTextBox.Text = $existingDataHash['Location']
}
$form.Controls.Add($locationTextBox)
$doorTagLabel = New-Object System.Windows.Forms.Label
$doorTagLabel.Text = "Door Tag #:"
$doorTagLabel.Location = New-Object System.Drawing.Point(10, 320)
$doorTagLabel.AutoSize = $true
$doorTagLabel.MaximumSize = New-Object System.Drawing.Size(380, 0)
$form.Controls.Add($doorTagLabel)
$doorTagTextBox = New-Object System.Windows.Forms.TextBox
$doorTagTextBox.Location = New-Object System.Drawing.Point(150, 320)
if ($existingDataHash.ContainsKey('Door Tag #')) {
$doorTagTextBox.Text = $existingDataHash['Door Tag #']
}
$form.Controls.Add($doorTagTextBox)
$imprivataLabel = New-Object System.Windows.Forms.Label
$imprivataLabel.Text = "Imprivata Type:"
$imprivataLabel.Location = New-Object System.Drawing.Point(10, 360)
$imprivataLabel.AutoSize = $true
$imprivataLabel.MaximumSize = New-Object System.Drawing.Size(380, 0)
$form.Controls.Add($imprivataLabel)
$imprivataComboBox = New-Object System.Windows.Forms.ComboBox
$imprivataComboBox.Items.AddRange(@("Type 1", "Type 2"))
$imprivataComboBox.Location = New-Object System.Drawing.Point(150, 360)
if ($existingDataHash.ContainsKey('Imprivata Type')) {
$imprivataComboBox.SelectedItem = $existingDataHash['Imprivata Type']
}
$form.Controls.Add($imprivataComboBox)
$bcaLabel = New-Object System.Windows.Forms.Label
$bcaLabel.Text = "BCA:"
$bcaLabel.Location = New-Object System.Drawing.Point(10, 400)
$bcaLabel.AutoSize = $true
$bcaLabel.MaximumSize = New-Object System.Drawing.Size(380, 0)
$form.Controls.Add($bcaLabel)
$bcaCheckBox = New-Object System.Windows.Forms.CheckBox
$bcaCheckBox.Location = New-Object System.Drawing.Point(150, 400)
if ($existingDataHash.ContainsKey('BCA') -and $existingDataHash['BCA'] -eq 'Yes') {
$bcaCheckBox.Checked = $true
}
$form.Controls.Add($bcaCheckBox)
# Create OK button
$okButton = New-Object System.Windows.Forms.Button
$okButton.Text = "OK"
$okButton.Location = New-Object System.Drawing.Point(150, 440)
$okButton.Add_Click({
# Ensure the AD user email is fetched before using it
$adUserEmail = $adUsers | Where-Object { $_.DisplayName -eq $userComboBox.SelectedItem } | Select-Object -ExpandProperty EmailAddress
# Validate form fields
if (-not $networkComboBox.SelectedItem) {
[System.Windows.Forms.MessageBox]::Show("Please select a network.")
return
}
if (-not $userComboBox.SelectedItem) {
[System.Windows.Forms.MessageBox]::Show("Please assign to an AD user.")
return
}
$selectedNetwork = $merakiNetworks | Where-Object { $_.name -eq $networkComboBox.SelectedItem }
$tags = @()
if ($departmentTextBox.Text) {
$tags += $departmentTextBox.Text
}
if ($wowCheckBox.Checked) {
$tags += "WOW"
}
if ($bcaCheckBox.Checked) {
$tags += "BCA"
}
if ($imprivataComboBox.SelectedItem) {
$tags += "Imprivata_$($imprivataComboBox.SelectedItem -replace ' ', '_')" # Remove spaces from Imprivata tag
}
# Concatenate notes into a single string
$notes = ""
if ($phoneTextBox.Text) {
$notes += "Phone Extension: $($phoneTextBox.Text)`n"
}
if ($locationTextBox.Text) {
$notes += "Location: $($locationTextBox.Text)`n"
}
if ($doorTagTextBox.Text) {
$notes += "Door Tag: $($doorTagTextBox.Text)`n"
}
if ($adUserEmail) {
$notes += "Owner: $adUserEmail"
}
# Get the serial number of the computer
$serialNumber = Get-ComputerSerialNumber
# Call the Meraki API to update the device notes and tags
$url = "https://api.meraki.com/api/v1/devices/$serialNumber"
$headers = @{
"X-Cisco-Meraki-API-Key" = $apiKey
"Content-Type" = "application/json"
}
$body = @{
name = $systemName
notes = $notes
tags = $tags
serial = $serialNumber
} | ConvertTo-Json
try {
$response = Invoke-RestMethod -Method Put -Uri $url -Headers $headers -Body $body
[System.Windows.Forms.MessageBox]::Show("Device updated successfully.")
} catch {
$errorMessage = "Failed to update device: $_`n`nOrganization ID: $orgId`nNetwork ID: $($selectedNetwork.id)`nURL: $url`nHeaders: $($headers | ConvertTo-Json)`n`nData being pushed to the API:`n$body"
$errorForm = New-Object System.Windows.Forms.Form
$errorForm.Text = "Error"
$errorForm.Size = New-Object System.Drawing.Size(400, 300)
$errorForm.StartPosition = "CenterScreen"
$errorTextBox = New-Object System.Windows.Forms.TextBox
$errorTextBox.Multiline = $true
$errorTextBox.ReadOnly = $false
$errorTextBox.ScrollBars = "Vertical"
$errorTextBox.Text = $errorMessage
$errorTextBox.Dock = "Fill"
$errorForm.Controls.Add($errorTextBox)
$errorForm.ShowDialog()
}
# Write data to the file
$fileData = @"
System Name: $systemName
Serial Number: $serialNumber
Assigned User: $adUserEmail
Phone Extension: $($phoneTextBox.Text)
Department: $($departmentTextBox.Text)
WOW: $($wowCheckBox.Checked)
Location: $($locationTextBox.Text)
Door Tag #: $($doorTagTextBox.Text)
Imprivata Type: $($imprivataComboBox.SelectedItem)
BCA: $($bcaCheckBox.Checked)
"@
Write-SystemInfo -data $fileData
$form.Close()
})
$form.Controls.Add($okButton)
# Show the form
$form.ShowDialog()