• Recent
    • Unsolved
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Register
    • Login
    1. Home
    2. JJ Fullmer
    3. Posts
    • Profile
    • Following 5
    • Followers 4
    • Topics 55
    • Posts 947
    • Best 254
    • Controversial 0
    • Groups 3

    Posts made by JJ Fullmer

    • RE: powershell snapin no output, non error

      @lebrun78 Had another idea.
      Since it does work with the system account in the psexec shell you try the scheduled task method again and have it use the system account. It would look something like below

      Also, matching just ‘IPV4’ from the firmware boot options may not yield the most reliable results as not every device will name their network boot option like that. Another option that may work would be finding what your various models do use for pxe identification and script finding that one within this script, that would take some effort to maintain but would always get you the options you want
      Another possibility is finding all possible choices and putting them all above the windows boot manager. For example, I have a custom built pc that has 3 options related to network boot (excluding ipv6) options, I could find each of those guids and put them all at the top of the list so each one will be tried at boot. I altered the script below to use that method matching the things I found in a couple places (‘IPV4’, ‘Network’, ‘pxe’) and I switched the matching up using a method I found here https://stackoverflow.com/questions/16903460/bcdedit-bcdstore-and-powershell to parse the enum of firmware into a powershell object, it’s not a perfect conversion to an object but it’s workable.

      Another other option would be to get the tftpboot pxe files locally and add them to the efi partition and have it try a couple known ones, this method would work nicely as well especially on devices that don’t have built-in ethernet as they won’t have to support pxe boot to get to the bootfile that will get you into fog. But to make that work in a way that would support multiple pxe boot file options is a bit out of scope of this post.

      #start the task 10 seconds after its defined
      $trigger = New-ScheduledTaskTrigger -Once -at ((Get-date).AddSeconds(10))
      $settings = New-ScheduledTaskSettingsSet -WakeToRun -Priority 0;
      #make sure temp path exists, and use temp path to avoid issues with permissions on root of C
      if (!(Test-Path 'C:\temp')) { mkdir 'C:\temp'; }
      #use system account
      $principal = New-ScheduledTaskPrincipal -UserId 'NT AUTHORITY\SYSTEM' -RunLevel Highest -LogonType ServiceAccount;
      $sb = {
          $Configs   = New-Object -TypeName System.collections.generic.List['System.Object']
          $NameArray = @()
          $Pattern = '^(?<name>[a-z]*)?\s*(?<value>.*)?$'
          $firmware = (bcdedit /enum firmware);
          foreach ($item in $firmware ){
              if ( $item.trim() ){
                  $res = [regex]::matches( $item, $pattern )
                  if ( $res ){
                      $Value = $res[0].Groups['value'].value
                      $Name  = $res[0].Groups['name'].value
                      if ( $Value ){
                          if ( $Name ){
                              "$item creating pso" | out-host;
                              $PSO = [PSCustomObject]@{
                                  Name  = $Name
                                  Value = $Value
                              }
                              $NameArray += $PSO
                          } else {
                              if ( $NameArray.count ){
                                  ( $NameArray | Select-Object -last 1 ).Value += "; $Value"
                              }
                          }
                      }
                  }
              } else {
                  if ( $NameArray ){
                      $Configs.add(($NameArray))
                      $NameArray = @()
                  } 
              }
          }
          #the above loop from the example I found doesn't add the final item, so run this bit one more time
          if ( $NameArray ){
              $Configs.add(($NameArray))
              $NameArray = @()
          } 
          $netbootsipv4 = $configs | Where-Object { $_.Value[-1] -match "Network" -OR $_.Value[-1] -match "PXE" -OR $_.Value[-1] -match "IPV4" } | Where-Object { $_.Value[-1] -notmatch "IPV6" }
          #loop through the found netboot options and put each one at the top so all of them are before windows boot
          $result = New-Object -TypeName System.collections.generic.List['System.Object']
          $netbootsipv4 | ForEach-Object {
              $GUID = $_.Value[0];
              $bootOption = "Adding Desc: $($_.Value[1]) GUID: $GUID to boot first";
              $addBootToTop = (bcdedit /set "{fwbootmgr}" displayorder $GUID /addfirst);
              $result.add($bootOption);
              $result.add($addBootToTop);
          }
          #now that for sure all pxe boot options are first boots, try to find the active connection that matches and make it for sure the first boot option
          $upNets = (get-netadapter | Where-Object status -eq up).InterfaceDescription
          $upNets | ForEach-Object {
              #set the current item to a variable
              $adapterName = $_;
              #see if the active adapter's description matches any of the found ipv4 boot options
              $adapterBootEntry = $netbootsipv4 | Where-Object { $_.Value[-1] | Select-String -pattern $adapterName -SimpleMatch}
              if ($adapterBootEntry) {
                  $guid = $adapterBootEntry.Value[0]
                  $bootOption = "This interface is active, put it towards the top - Adding Desc: $($_.Value[1]) GUID: $GUID to boot first";
                  $addBootToTop = (bcdedit /set "{fwbootmgr}" displayorder $GUID /addfirst);
                  $result.add($bootOption);
                  $result.add($addBootToTop);
              }
          }
          
          $newBootOrder = (bcdedit /enum "{fwbootmgr}");
          #make c:\temp if it doesn't exist
          if (!(Test-Path 'C:\temp')) { mkdir 'C:\temp'; }
          #log everything in a new C:\temp\firmware.log file
          $resultString = "Firmware: $($firmware | out-string)`n`nresult: $($result | out-string)`n`nNewBootOrder: $newBootOrder`n"
          New-Item -path C:\temp\firmware.log -itemType File -value $resultString -force;
      }
      $netBootScript = "C:\temp\netboot.ps1";
      New-Item $netBootScript -value $sb.tostring() -force;
      $action = New-ScheduledTaskAction -Execute "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -Argument "-File 'C:\temp\netboot.ps1'"
      $task = New-ScheduledTask -Action $action -Description "update bcd" -Principal $principal -Trigger $trigger -Settings $settings;
      $taskName = "boot-to-IPV4";
      #register the task
      Register-ScheduledTask -InputObject $task -TaskName $taskName;
      # force the task to start
      Start-ScheduledTask -TaskName $taskName;
      # wait for the task to finish
      while ((Get-ScheduledTask $taskName).State -eq 'Running') {
          Start-Sleep -Seconds 1;
      }
      #delete the task
      Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -EA 0;
      #delete the created script file
      remove-Item $netBootScript -force -ea 0;
      

      I saved the above as a .ps1 file and made it a snapin and it worked for me when I tested it on a vm. However, it did make it so when fog booted to the hard disk via refind it wasn’t able to boot into windows. This may be related to my own custom configs in refind, but something to test for on a few different platforms to be on the safe side.
      Wait wait, I made a small change to the logging and updated the snapin and then it didn’t work the second time. That’s odd… I recreated and couldn’t get it to work a second time. There’s something weird going on for sure. I know I double checked that things were not already configured to boot to the network the time it did work.
      Granted, since it’s happening via scheduled task, if you have an AD environment you could deploy this script in a scheduledTask via a gpo. Add the file (the one created from the $sb in the script) to computers with file preferences and create the scheduled task that runs as the system account using a scheduled task gpo.

      Will have to dig more into this on why the script isn’t doing anything via snapin, maybe @Sebastian-Roth can help a bit on troubleshooting the client side. And maybe other @testers could test using this script as a snapin too to see what results are being had elsewhere.

      Then just as an fyi to anyone reading this, you can revert back to the normal windows boot manager with (bcdedit /set "{fwbootmgr}" displayorder "{bootmgr}" /addfirst);

      posted in Windows Problems
      JJ FullmerJ
      JJ Fullmer
    • RE: Powershell API Module

      A minor bugfix update has been released

      Release notes: https://github.com/darksidemilk/FogApi/releases/tag/2304.5.41
      PSgallery Listing: https://www.powershellgallery.com/packages/FogApi/2304.5.41

      posted in Tutorials
      JJ FullmerJ
      JJ Fullmer
    • RE: powershell snapin no output, non error

      @lebrun78 As I mentioned that method would only work if the admin is logged in. If you’re running this at deploy time and have other firstlogoncommands that run via an unattend.xml file then it would work when the computer is first imaged to change the boot order back to pxe boot first.

      There is a feature request that’s being considered for making it so the fogclient can handle changing the boot order, but no idea when there will be the time for that to be implemented. You’re welcome to fork that repo and give it a go 🙂

      Personally I made an internal powershell module that queues the image using the FogApi powershell module (see my signature) and runs a remote powershell command on the machine I’m imaging to download the latest ipxe.efi (or snponly.efi) and I set that as the bootfile (I find it hard to match the network boot option as it’s labeled different on different platforms in my environment, but booting directly to the efi boot file always works).

      posted in Windows Problems
      JJ FullmerJ
      JJ Fullmer
    • RE: powershell snapin no output, non error

      @lebrun78 It should still be creating that log file of C:\temp\firmware.log
      What does that log say after running this?

      posted in Windows Problems
      JJ FullmerJ
      JJ Fullmer
    • RE: powershell snapin no output, non error

      @lebrun78 I just noticed that you’re running the command line step by step in the tests.
      The best thing to do would be put a copy of the file at C:\Program Files (x86)\FOG\tmp\boot-uefi.ps1 and run it from there as the system account for a test of the full context of how it will run. You may want to run get-service fogservice | stop-service first so that the fogservice doesn’t delete the tmp folder while you’re testing. To be fully ideal you should stop the service right after it downloads the script so that the permissions of the file created by the service are fully replicated, but that can be tricky

      posted in Windows Problems
      JJ FullmerJ
      JJ Fullmer
    • RE: powershell snapin no output, non error

      @lebrun78 I don’t have a windows 11 host to test with. Maybe there’s some new security feature in windows 11 related to bcdedit and background services even running as the system account. It doesn’t make a lot of sense.

      Here’s another idea, it’s a bit crazy and will only work as part of firstlogoncommands after imaging (assuming you’re using sysprep with a firstlogoconcommands and auto logon of the admin user). This is when attached snapins will start applying anyway.

      This script will essentially create a scheduled task that runs on demand as the built-in administrator named ‘administrator’ interactively. So it will open the powershell window in the logged in session in admin context, this only works with the built-in administrator in my experience and hopefully it still works in windows 11.

      $adminUsr = "$($ENV:ComputerName)\Administrator"
      # create the scheduled task
      $trigger = New-ScheduledTaskTrigger -AtLogOn -User $adminUsr;
      $settings = New-ScheduledTaskSettingsSet -WakeToRun -Priority 0;
      $principal = New-ScheduledTaskPrincipal -UserId $adminUsr -RunLevel Highest -LogonType Interactive;
      $sb = { 
      $firmware = (bcdedit /enum firmware);
      $fullLine = (($firmware | Select-String "IPV4" -Context 1 ).context.precontext)[0];
      $GUID = '{' + $FullLine.split('{')[1];
      $result = (bcdedit /set "{fwbootmgr}" displayorder $GUID /addfirst);
      #make c:\temp if it doesn't exist
      if (!(Test-Path 'C:\temp')) { mkdir 'C:\temp'; }
      #log everything in a new C:\temp\firmware.txt file
      New-Item -path C:\temp\firmware.txt -itemType File -value "Firmware: $($firmware | out-string)`n`nFullLine: $fullLine`nGUID: $GUID`nresult: $result`n" -force;
      }
      New-Item C:\netboot.ps1 -value $sb.tostring() -force;
      $action = New-ScheduledTaskAction -Execute powershell.exe -Argument "-File 'C:\netboot.ps1'"
      $task = New-ScheduledTask -Action $action -Description "update bcd" -Principal $principal -Trigger $trigger -Settings $settings;
      $taskName = "boot-to-net"
      Register-ScheduledTask -InputObject $task -TaskName $taskName;
      Start-ScheduledTask -TaskName $taskName;
      while ((Get-ScheduledTask $taskName).State -eq 'Running') {
          Start-Sleep -Seconds 1;
      }
      Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -EA 0;
      Remove-Item 'C:\netboot.ps1' -force -ea 0
      
      posted in Windows Problems
      JJ FullmerJ
      JJ Fullmer
    • RE: powershell snapin no output, non error

      @lebrun78 Well that’s odd for sure.
      You can use psexec top open up a system user shell. The tool may be flagged by your anti-virus software because it can give system user access, but it’s made by microsoft for this type of scenario. It’s a portable exe, no install needed.

      Once you have psexec

      # open up a command prompt as system user
      path\to\psexec.exe -i -s cmd.exe
      # this will open a cmd in a new window
      # in the new window, enter powershell
      powershell
      #confirm you are the system account
      WhoAmI
      #this should display: nt authority\system
      

      From there you can run the script and see what happens.

      I can’t imagine bcdedit can’t be used by the system user. When I run the commands as the system user it appears to be working as expected.

      There could be some other weirdness with one of 2 things:

      • 32 vs 64 bit version of powershell (you can specify a different path to powershell in your snapin definition, the default is C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe but there’s also a version at C:\windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe
      • You could need to use start-process to call the bcdedit tool and specify the full path but that makes it more difficult to get the output into a variable, but not impossible, i.e.
      $bcdedit = "C:\windows\System32\bcdedit.exe"
      $firmware = & {start-process -filepath $bcdedit -args "/enum firmware" -Wait -RedirectStandardOutput output.txt ; get-content output.txt; remove-item output.txt}
      $fullLine = (($firmware | Select-String "IPV4" -Context 1 ).context.precontext)[0]
      $GUID = '{' + $FullLine.split('{')[1]
      $result = & {start-process $bcdedit -args "/set `"{fwbootmgr}`" displayorder $GUID /addfirst" -Wait -RedirectStandardOutput output.txt ; get-content output.txt; remove-item output.txt}
      #make c:\temp if it doesn't exist
      if (!(Test-Path 'C:\temp')) { mkdir 'C:\temp' }
      #log everything in a new C:\temp\firmware.txt file
      New-Item -path C:\temp\firmware.txt -itemType File -value "Firmware: $($firmware | out-string)`n`nFullLine: $fullLine`nGUID: $GUID`nresult: $result`ndate: $datetime`n" -force
      
      posted in Windows Problems
      JJ FullmerJ
      JJ Fullmer
    • RE: VM won't boot from PXE on proxmox

      @ordinatous said in VM won’t boot from PXE on proxmox:

      @JJ-Fullmer
      Yes FOG on esxi , but I’m sure it’s not the problem.

      I’m not saying it’s a problem, it’s a great way to host your fog server. I was suggesting making your VM to capture from on esxi instead of proxmox.

      posted in General Problems
      JJ FullmerJ
      JJ Fullmer
    • RE: powershell snapin no output, non error

      @lebrun78 said in powershell snapin no output, non error:

      L’opération a réussi.

      Maybe try it without the text file? And without calling cmd? But for being sure it did something we can try adding a more verbose log file

      $firmware = (bcdedit /enum firmware)
      $fullLine = (($firmware | Select-String "IPV4" -Context 1 ).context.precontext)[0]
      $GUID = '{' + $FullLine.split('{')[1]
      $result = (bcdedit /set "{fwbootmgr}" displayorder $GUID /addfirst)
      #make c:\temp if it doesn't exist
      if (!(Test-Path 'C:\temp')) { mkdir 'C:\temp' }
      #log everything in a new C:\temp\firmware.txt file
      New-Item -path C:\temp\firmware.txt -itemType File -value "Firmware: $($firmware | out-string)`n`nFullLine: $fullLine`nGUID: $GUID`nresult: $result`n" -force
      
      posted in Windows Problems
      JJ FullmerJ
      JJ Fullmer
    • RE: VM won't boot from PXE on proxmox

      @ordinatous The fog installer handles compiling the ipxe binaries and putting them in the /tftpboot folder and serving them. You may have an additional tftp server or service configured if you also have this /srv/tftp folder and it may be conflicting with the FOG system.
      @Sebastian-Roth or @george1421 might be able to help a bit more on how to get around an extra tftp server/service running. It’s a bit confusing here as it looks like you do have the fog /tftpboot files in what file is downloaded by the pxe boot.

      Also are you saying in that last post that it did get to the fog menu?

      I also just noticed you’re on a slightly older version of FOG. I would suggest rerunning the installer. i.e. if you used the git version (it appears you did based on the version 1.5.9.154 which is a dev branch version) you just cd to where you cloned it (typically /root/fogproject) and you run this on the fog server to update to the latest dev-branch version

      # cd to where the git repo was cloned
      cd /root/fogproject
      #update all branches
      git fetch --all
      #switch to dev-branch
      git checkout dev-branch
      #run a git pull for good measure
      git pull
      # run the installer
      sudo -i
      cd bin
      ./installfog.sh
      

      see also https://docs.fogproject.org/en/latest/installation/install-fog-server/

      The installer will recompile the latest ipxe files and put them in /tftpboot and you give it another go

      posted in General Problems
      JJ FullmerJ
      JJ Fullmer
    • RE: UEFI vs fog

      @jeremyvdv The vendor classes bit in dhcp is only required when you need some hosts to boot with bios and others to boot with uefi. If everything uses uefi then you can just set option 66/67 on the server or scope options. Server options will apply to all dhcp scopes in one spot, but you can also apply it more modularing in dhcp scope options.

      I would suggest trying ipxe.efi instead of snponly.efi and see if that makes a difference.

      posted in Hardware Compatibility
      JJ FullmerJ
      JJ Fullmer
    • RE: FOG Post install script for Win Driver injection

      @Coolguy3289 Sorry for the crazy delayed reply, I just saw this, I must have missed the notification

      This is what my general structure looks like. You would not need to recreate that with the + pseudo wildcards, that is part of my windows side matching that I have now actually replaced with this modellist method. The grep line will search each model folder in the given make folder regardless of its name. I will probably end up changing those folders to be something like HP ElitePro G# or something like that.

      e880c851-3f3c-418d-adb9-8ff886199667-image.png

      Then say I have a HP Elite Mini 600 G9 Desktop PC

      This bit

      makePth="/images/drivers/${make}"
      cd $makePth;
      

      Should get me into the /images/drivers/hp folder

      Then this listFile=`grep -il "$model" ./*/*-ModelList.txt` should search each HP ModelList.txt for that model string using grep and return the matching modelList file with a match. In this case it would match /images/drivers/hp/HP + +00 G9+/HP + +00 G9+-ModelList.txt as that file looks like this where I have various possible matching model names these drivers apply to including the one being searched for

      HP Elite Mini 600 G9 Desktop PC
      HP Elitedesk 600 G9 DM
      HP Elite Mini 800 G9 Desktop PC
      HP Pro Mini 400 G9 Desktop PC
      HP EliteOne 840 23.8 inch G9 All-in-One Desktop PC
      

      And then this remotedriverpath="$makePth/${listFile%/*}" should point to the parent path of where the found ModelList.txt file was found and it will then proceed to inject that folder to `C:\Out-Of-Box Drivers\HP + +00 G9+

      And as to creating those modelList.txt files, it’s easier than it sounds, especially if you use the fog api powershell module FogApi (see my signature).

      For example, to get all my current HP (and any other make that stores the friendly name of their model in system-product-name) model names as they are detected by fog you would setup the FogApi module in powershell

      Install-Module FogApi;
      Set-FogServerSettings -interactive
      

      Then this one liner would get all your fog host inventories, select just the sysman and sysproduct fields, and then sort it to unique model names giving you a list of model names you have in your inventory

      (get-foghosts).inventory | select-object sysman,sysproduct | sort-object sysproduct -Unique
      

      You’d also what to run these other 2 commands to get lists of other model names stored in system-version or baseboard-product-name

      (get-foghosts).inventory | select-object sysman,sysversion | sort-object sysversion -Unique
      (get-foghosts).inventory | select-object sysman,mbproductname | sort-object mbproductname -Unique
      

      You could probably expound from there to programatically create to folder structure and the modelList files of your current hosts, but I don’t have time at the moment to get that deep into it. I don’t have that many different models and already had the folder structure so just getting those lists was all I needed to construct the model list files

      posted in Tutorials
      JJ FullmerJ
      JJ Fullmer
    • RE: VM won't boot from PXE on proxmox

      @ordinatous You mentioned you have the fog server on esxi. Is putting your image vm on esxi an option? That’s where I keep mine and it works rather well. I just keep it turned off when it’s not being configured and captured.

      posted in General Problems
      JJ FullmerJ
      JJ Fullmer
    • RE: Powershell API Module

      A minor update has been released

      Release notes: https://github.com/darksidemilk/FogApi/releases/tag/2303.5.33
      PSgallery Listing: https://www.powershellgallery.com/packages/FogApi/2302.5.33

      posted in Tutorials
      JJ FullmerJ
      JJ Fullmer
    • RE: HP EliteBook 840 G9 - Cannot deploy image

      @Sebastian-Roth So I think I found something related to this that may be a bug.

      We recently got some Lenovo x1 yoga gen7’s. They have an internal mac with the pass through option, but usb-c or usb adapter is the only option.

      When I have pass through on and register the host with a usb-c mac it registered the host with the passed-through address.
      When I then reboot to re-image, the pxe boot menu detected the usb mac instead and said the host wasn’t registered
      I added the usb mac to the host manually as a second mac and then it worked fine.
      So at different points in the process of pxe boot, it gets a different mac when pass-through is enabled, I’d expect it to always use the passed-through mac.

      I don’t know if this is a new hardware behavior or new fog behavior. I have another lenovo laptop I need to image today that also does the mac passthrough. I’ll do some testing with that one if I have the time.

      posted in Hardware Compatibility
      JJ FullmerJ
      JJ Fullmer
    • RE: VM won't boot from PXE on proxmox

      @ordinatous For using ipxe.efi you need to change the VM settings to use uefi instead of bios/legacy mode. I’m not familiar with proxmox but I found this related guide https://blog.hadenes.io/post/convert-a-proxmox-windows-guest-from-bios-to-uefi/#:~:text=Open the Proxmox web interface and navigate to,if you plan to upgrade to Windows 11)
      It’s not all relevant as it starts with how to convert the windows install (if you already installed windows in bios mode, I would suggest redoing it in uefi mode unless all your hardware can only use bios mode). But this bit is how to change to uefi mode.

      Open the Proxmox web interface and navigate to the options for the guest machine.
      Add a new “EFI disk” to the guest machine, making sure to enable the “Pre-Enrolled-Keys” option.
      Add a new “TPM” device to the guest machine. (only necessary if you plan to upgrade to Windows 11)
      Change the “BIOS” option from “SeaBIOS” to “OVMF”.

      posted in General Problems
      JJ FullmerJ
      JJ Fullmer
    • RE: VM won't boot from PXE on proxmox

      @ordinatous Is it just freezing after that last screenshot you added? It looks correct up to that point. Are you able to boot to the fog menu from a physical device?

      posted in General Problems
      JJ FullmerJ
      JJ Fullmer
    • RE: VM won't boot from PXE on proxmox

      @ordinatous
      Have you tried using uefi mode and using ipxe.efi or snponly.efi?
      Or if you must use legacy, have you tried a different pxe bootfile like undionly.kkpxe or ipxe.kpxe ? You just need to specify a different file in option 66

      posted in General Problems
      JJ FullmerJ
      JJ Fullmer
    • RE: Surface Go 1 can't access fog host variables in FOS during postdownload scripts

      @Sebastian-Roth Just an FYI, I ran into this again on different hardware. An HP Z2 G4 Workstation did the same thing.
      There may have been more to it though, still troubleshooting a bit. My failsafe in the postdownload script to have it still join the domain through the unattend file was injected into the file, but the computer did not join the domain through that method, there may have been a different reason for that though.

      Point being, there could be other hardware that weirdly doesn’t get all the variables. I sadly can’t super test on this hardware as I discovered it when imaging a production machine that I can’t take out of production for testing. But I can probably find a chance to get the fos level device info or something.

      posted in Bug Reports
      JJ FullmerJ
      JJ Fullmer
    • RE: Capture not the entire disk but more than one partition

      As a side note:
      The image type description in the wiki (I got there from the balloon help: https://wiki.fogproject.org/wiki/index.php?title=Managing_FOG#Images) points to a non-existing page (404): https://docs.fogproject.org/en/latest/management/image-management.html

      Thanks! @JJ-Fullmer would you find the time to fix that quickly?

      Fixed!
      @medchemii thanks for letting us know. We changed the structure of the new docs page a bit and it affected the links. We’re working on getting redirection working for old links still

      posted in FOG Problems
      JJ FullmerJ
      JJ Fullmer
    • 1
    • 2
    • 7
    • 8
    • 9
    • 10
    • 11
    • 47
    • 48
    • 9 / 48