Advertisement
Guest User

Replace GUID/UUID for Meraki Wireless Clients

a guest
Nov 12th, 2019
505
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <#
  2. update_client_hostnames - Check Meraki wireless clients for a GUID/UUID in the description, if found change the name to the hostname in DHCP.
  3. You will need to create a scheduled task to run this script that checks every x minutes / hours. We have it check hourly.
  4.  
  5. Important Note:
  6.  
  7.  * This script does not handle devices that have been renamed after replacing the GUID/UUID with the hostname found in DHCP. You'll need to manually
  8.    update these clients or write a script to do it ;)
  9.  
  10.  * This script will only grab a max of 1000 clients seen in the last 4 hours
  11.  
  12. To 'secure' your Meraki API Key:
  13.  
  14. The script below in 'CUT BELOW / ABOVE' can be used to generate a 'secure' password.
  15. It means you can store your meraki api key under an account not used for logons and
  16. create a task schedule with the credentials of that account. If anyone else
  17. tries to read the file they won't be able to unless they logon under the account
  18. that created the file. When prompted for a username you can leave it blank or put
  19. whatever in as only the password portion is used to store the meraki api key.
  20.  
  21. ---- CUT BELOW ----
  22. # Instead of storing the plain text password to disk
  23. #
  24. # If anyone besides the user whom created the password tries to load the secure file, it won't work.
  25. # It'll give a "Key not valid for use in specified state." exception
  26. $cred = get-credential
  27. $username = $cred.GetNetworkCredential().UserName
  28. $password = $cred.GetNetworkCredential().Password
  29.  
  30.  
  31. $secureStringPwd = $password | ConvertTo-SecureString -AsPlainText -Force
  32. $creds = New-Object System.Management.Automation.PSCredential -ArgumentList $username, $secureStringPwd
  33. $secureStringText = $secureStringPwd | ConvertFrom-SecureString
  34.  
  35. # You can either save this to $password and use read.ps1 to read it back in or save it to a file and read it back in and convert to a pscredential object
  36.  
  37. $file = "$PSScriptRoot\secure.txt"
  38. Set-Content $file $secureStringText
  39.  
  40. $username = $null
  41. $password = $null
  42.  
  43.  # ---- CUT ABOVE ----
  44.  
  45. ^^ The above script is needed at the below part in this script:
  46.  
  47. # retrieve apikey - to create the secure.txt file see above comments for the script
  48. $file = "$PSScriptRoot\secure.txt"
  49. $pass = Get-Content $file
  50. $securePwd = $pass | ConvertTo-SecureString
  51. $cred = New-Object System.Management.Automation.PSCredential("None", $securePwd)
  52. $apikey = $cred.GetNetworkCredential().Password
  53. $cred = $null
  54.  
  55.  #
  56.  # Helpful links if you get stuck:
  57.  #
  58.  # The Cisco Meraki Dashboard API -> Need to turn your Meraki API on if it isn't already and generate a Meraki API key
  59.  # https://documentation.meraki.com/zGeneral_Administration/Other_Topics/The_Cisco_Meraki_Dashboard_API
  60.  #>
  61.  
  62. <#
  63.  # Logging information.
  64.  #>
  65. function logger($msg, $show_to_screen=$true) {
  66.     # log to file and optionally to screen by default
  67.     $out = "$(Get-Date -UFormat "%Y-%b-%d %T") - $msg"
  68.     if ($show_to_screen) {
  69.         write-host $out
  70.     }
  71.     add-content -Path "$PSScriptRoot\status.log" -Value $out -Force
  72. }
  73.  
  74. function ApiError($url=$null) {
  75.    
  76.     $err_url = ""
  77.     if ($url -ne $null) {
  78.         $err_url = " [$url]"
  79.     }
  80.  
  81.     $msg = "Exception$err_url::Status Code: " + $_.Exception.Response.StatusCode.value__ + " - " + $_.Exception.Response.StatusDescription
  82.     Write-Host -BackgroundColor:Black -ForegroundColor:Red $msg
  83.     logger($msg, $false)
  84. }
  85.  
  86. function api_get($key, $url) {
  87.     $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
  88.     $headers.Add("X-Cisco-Meraki-API-Key", "$apikey")
  89.     #$headers.add("Content-Type", "application/json")
  90.  
  91.     try {
  92.         $response = Invoke-RestMethod -Uri $url -Headers $headers -ContentType "application/json" -Method Get
  93.     }
  94.     catch {
  95.         ApiError($url)
  96.     }
  97.     return $response
  98. }
  99.  
  100. #
  101. # api_push - Pushes a change to the Meraki Cloud
  102. #
  103. function api_push($apikey, $url, $json, $method) {
  104.     # Info on why this is necessary is at -> https://documentation.meraki.com/zGeneral_Administration/Other_Topics/The_Cisco_Meraki_Dashboard_API
  105.     $headers = @{
  106.         "X-Cisco-Meraki-API-Key" = "$apikey"
  107.     }
  108.  
  109.     # Handle Redirection - Ugly, but it won't work without picking up the redirected url
  110.     $response = Invoke-WebRequest -Uri $url -Headers $headers -ContentType "application/json" -Method $method -Body $json -MaximumRedirection 0
  111.     $redirect = $response.Headers.Location
  112.     try {
  113.         $response = Invoke-WebRequest -Uri $redirect -Headers $headers -Method $method -Body $json -ContentType "application/json"
  114.     }
  115.     catch {
  116.         ApiError($url)
  117.     }
  118.  
  119.     return $response
  120. }
  121.  
  122. #
  123. # api_post - friendly function as there is post or put, put removed as it isn't needed in this script
  124. #
  125. function api_post($apikey, $url, $json) {
  126.     return api_push $apikey $url $json "Post"
  127. }
  128.  
  129. # retrieve apikey - to create the secure.txt file see above comments for the script
  130. $file = "$PSScriptRoot\secure.txt"
  131. $pass = Get-Content $file
  132. $securePwd = $pass | ConvertTo-SecureString
  133. $cred = New-Object System.Management.Automation.PSCredential("None", $securePwd)
  134. $apikey = $cred.GetNetworkCredential().Password
  135. $cred = $null
  136.  
  137. # Turn on TLS 1.2 for Invoke-RestMethod
  138. [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
  139.  
  140. # base url for api calls
  141. $base_url = "api.meraki.com/api/v0"
  142. # Name of your wireless network
  143. $wireless_network_name = "***NAME OF YOUR WIRELESS NETWORK NOT SSID***"
  144.  
  145. # Fetch Organisation ID
  146. write-host "Fetch Organisation ID..." -NoNewline
  147. # API INFO - https://developer.cisco.com/meraki/api/#/rest/api-endpoints/organizations/get-organization
  148. $response = api_get $apikey "https://$base_url/organizations"
  149. $org_id = $response.id
  150.  
  151. # Fetch Network IDs
  152. write-host "Fetch Network IDs..." -NoNewline
  153. # API INFO - https://developer.cisco.com/meraki/api/#/rest/api-endpoints/networks/get-organization-networks
  154. $value = api_get $apikey "https://$base_url/organizations/$org_id/networks"
  155. write-host "Done!`n" -NoNewline
  156.  
  157. # Get the X Wireless network ID
  158. # Make sure to replace ***NAME OF YOUR WIRELESS NETWORK NOT SSID*** above with your wireless network name found in the Meraki Cloud dashboard
  159. $network_id = $null
  160. foreach ($v in $value) {
  161.     if ($v.name.Contains($wireless_network_name)) {
  162.         $network_id = $v.id
  163.         break
  164.     }
  165. }
  166.  
  167. <#
  168.     [
  169.         [
  170.             IPAddress       : x.x.x.x
  171.             ScopeId         : x.x.x.x
  172.             Description     :
  173.             ClientId        : XX-XX-XX-XX-XX-XX
  174.             HostName        : XXXX.XXX.XXX
  175.             ClientType      : Dhcp
  176.             AddressState    : Active
  177.             DnsRR           : AandPTR
  178.             DnsRegistration : Complete
  179.             LeaseExpiryTime : 24/08/2019 11:58:03 AM
  180.             NapCapable      : x
  181.             NapStatus       : x
  182.             ProbationEnds   :
  183.             PolicyName      :
  184.             ServerIP        : X.X.X.X
  185.         ],
  186.         ...
  187.     ]
  188. #>
  189. # DHCP Wifi Scope 1
  190. # computername - the server that issues the DHCP lease
  191. # scopeid - Replace X.X.X.X with the network address of the wifi network e.g. in DHCP it might look like 'Scope [192.168.0.0] ...'
  192. $dhcp_leases = get-dhcpserverv4lease -computername SERVER_NAME_HERE -scopeid X.X.X.X
  193.  
  194. # DHCP Wifi Scope 2
  195. # computername - same as above
  196. # scopeid - same as above
  197. # this is just for additional wifi networks
  198. $dhcp_leases += get-dhcpserverv4lease -computername SERVER_NAME_HERE -scopeid X.X.X.X
  199.  
  200. if ($network_id -ne $null) {
  201.     <#
  202.     [
  203.         [
  204.             id                 : kXXXXXX
  205.             mac                : XX:XX:XX:XX:XX:XX
  206.             description        : XXXXXXXXXXX
  207.             ip                 : XX.XX.XX.XX
  208.             ip6                : XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXX
  209.             user               : host/XXXXXXXXXXXXXXX
  210.             firstSeen          : 2019-01-09T02:27:26Z
  211.             lastSeen           : 2019-08-21T02:03:49Z
  212.             manufacturer       : Intel
  213.             os                 : Windows 10
  214.             recentDeviceSerial : XXXX-XXXX-XXXX
  215.             recentDeviceName   : XXXX
  216.             recentDeviceMac    : XX:XX:XX:XX:XX:XX
  217.             ssid               : XXXXXX
  218.             vlan               : XX
  219.             switchport         :
  220.             usage              : @{sent=11991; recv=75431}
  221.         ],
  222.         ...
  223.     ]
  224.     #>
  225.     # 28800 = 8 hours
  226.     # 14400 = 4 hours
  227.     #
  228.     # this will only grab clients seen in the last 4 hours, as it is in seconds you can change it
  229.     # API INFO - https://developer.cisco.com/meraki/api/#/rest/api-endpoints/clients/get-network-clients
  230.     $clients = api_get $apikey "https://$base_url/networks/$network_id/clients?perPage=1000&timespan=14400"
  231.     $msg = "Clients: $($clients.Length)"
  232.     logger($msg)
  233.  
  234.     # Find the GUID/UUID pattern
  235.     foreach ($client in $clients) {
  236.         # match example: 16ff5ae6-48a8-4fbf-8486-1a6673ff0ee4
  237.         if ($client.description -match ".{8}-.{4}-.{4}.{12}") {
  238.             #$client | select description, lastSeen, user, ip
  239.             $mac = $client.mac -replace ':', '-'
  240.  
  241.             # look for a match in dhcp leases
  242.             $found = $false
  243.             foreach ($lease in $dhcp_leases) {
  244.                 # match MAC and IP to update 'provision' the hostname in Meraki
  245.                 if ($lease.ClientId -eq $mac -and $lease.IPAddress -eq $client.ip) {
  246.                     $hostname = $lease.HostName
  247.  
  248.                     # strip domain name # This is stripped from the DHCP lease info, it'll be different in your domain so update this
  249.                     if ($hostname -like "*.XXX.local") {
  250.                         $hostname = $hostname.split('.')[0]
  251.                     }
  252.                     $log = "Converted <desc: $($client.description) IP: $($client.ip) MAC: $($client.mac)> to DHCP IPv4 Hostname: $hostname"
  253.                     logger($log)
  254.  
  255.                     # perform the update of the computer's name by provisioning the device
  256.                     # API INFO - https://developer.cisco.com/meraki/api/#/rest/api-endpoints/clients/provision-network-clients
  257.                     # You may need to adjust the devicePoliy and groupPolicyId if using something other than the default
  258.                     # for your devices.
  259.                     $url = "https://$base_url/networks/$network_id/clients/provision"
  260.                     $data = @{
  261.                         mac=$client.mac
  262.                         name=$hostname
  263.                         devicePolicy="Normal"
  264.                         groupPolicyId="101"
  265.                     }
  266.                     $json = $data | ConvertTo-Json
  267.                     $value = api_post $apikey $url $json
  268.  
  269.                     $found = $true
  270.                 }
  271.             }
  272.  
  273.             if ($found -eq $false) {
  274.                 $msg = "No DHCP match found for <desc: $($client.description) IP: $($client.ip) MAC: $($mac)>"
  275.                 logger($msg)
  276.             }
  277.         }
  278.     }
  279. }
  280. else {
  281.     logger("Unable to find the network id for XXX")
  282. }
  283.  
  284. $apikey = $null
  285. $dhcp_leases = $null
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement