The PowerShell script discussed in this post allows you to deploy fonts within your Windows network.
Avatar

Many of my clients work in creative industries, so a regular task for us is to install fonts. These fonts might be licensed on a per-machine basis or simply used for only one project and never seen again. This makes it difficult or essentially impractical to build a machine with all the required fonts already installed.

So when a new project starts, or more likely the last minute before several machines head out to give important presentations, you may need to install 50 fonts on 10 machines! But the users have no time for you to log on to their PCs and install the fonts.

Over the years, I have put together what we will generously call some "solutions" to this problem. The most recent was so labor intensive, it was probably more effort than that saved by walking to each individual computer with a pen drive containing the font files.

I speak of course of the Group Policy Preference (GPP) settings. Installing a font with a GPP is quite easy. You can set the font file to copy from a source folder to the C:\Windows\Fonts folder, and in the same GPP, set up the relevant registry key. After a reboot, the font is available for use.

GPP font example

GPP font example

GPP font example registry details

GPP font example registry details

However, as you can see from the two example images, managing multiple font installs using this method (having to create each registry item) is time consuming.

Then when asked to install several more fonts on several more computers, I thought again about using PowerShell. I had investigated font installs with PowerShell before, but I had never found a method that worked for me. I don't remember every example of what I tried, but I do recall one that tried to force the User Account Control (UAC) elevation prompt via PowerShell. The whole thing just seemed a bit lacking, so I put it on the back burner. However, soon I was back again, needing a quick way to install fonts without messing around.

I realized the answer was in front of me. I could use PowerShell to populate the registry section of the GPP for me! Genius.

No—wait. Why use GPP at all if PowerShell can do the registry entries for me? Goal!

So I present to you Font-Install.ps1. The script has two parameters: $pcNames and $fontFolder.

The $pcNames array will accept a list of PC Names, and $fontFolder (as the name suggests) is a string that should contain a Uniform Naming Convention (UNC) path to where the script can find the font files themselves.

The script will test a connection to each PC in turn. It will copy each font file it finds available on the network from the $fontFolder to the C:\Windows\Fonts folder on the PC using the admin $ share. It does this from the machine where the script executes to mitigate the issue of credential hopping through remote PowerShell sessions.

It then processes each font file for the font name (which is not always the same as the file name) and creates a registry entry for that font using Invoke-Command.

Font Install.ps1

Font Install.ps1

This is the full script:

Subscribe to 4sysops newsletter!

param(
[Parameter(Mandatory=$true,Position=0)]
[ValidateNotNull()]
[array]$pcNames,
[Parameter(Mandatory=$true,Position=1)]
[ValidateNotNull()]
[string]$fontFolder
)
$padVal = 20
$pcLabel = "Connecting To".PadRight($padVal," ")
$installLabel = "Installing Font".PadRight($padVal," ")
$errorLabel = "Computer Unavailable".PadRight($padVal," ")
$openType = "(Open Type)"
$regPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts"
$objShell = New-Object -ComObject Shell.Application
if(!(Test-Path $fontFolder))
{
    Write-Warning "$fontFolder - Not Found"
}
else
{
    $objFolder = $objShell.namespace($fontFolder)
    foreach ($pcName in $pcNames)
    {
        Try{
            Write-Output "$pcLabel : $pcName"
            $null = Test-Connection $pcName -Count 1 -ErrorAction Stop
            $destination = "\\",$pcname,"\c$\Windows\Fonts" -join ""
            foreach ($file in $objFolder.items())
            {
                $fileType = $($objFolder.getDetailsOf($file, 2))
                if(($fileType -eq "OpenType font file") -or ($fileType -eq "TrueType font file"))
                {
                    $fontName = $($objFolder.getDetailsOf($File, 21))
                    $regKeyName = $fontName,$openType -join " "
                    $regKeyValue = $file.Name
                    Write-Output "$installLabel : $regKeyValue"
                    Copy-Item $file.Path  $destination
                    Invoke-Command -ComputerName $pcName -ScriptBlock { $null = New-ItemProperty -Path $args[0] -Name $args[1] -Value $args[2] -PropertyType String -Force } -ArgumentList $regPath,$regKeyname,$regKeyValue
                }
            }
        }
        catch{
            Write-Warning "$errorLabel : $pcName"
        }
    }
}
avatar
31 Comments
  1. Avatar
    DeployGuy (Rank 1) 5 years ago

    This is something I have been looking for, I had a similar experience when searching for  solution. What I will do with this is first test this in my environment, but then once I know my environment doesn’t break it, I will modify it to be used with SCCM. this will overcome the inherent limitation` of targeting workstations with Invoke-command, because the user has shutdown their workstation. With SCCM the job will just wait for the user to come on line and optionally require a reboot. Great work!

    • Avatar
      Om 4 years ago

      Hey How did you achieve this? could you please share the step by step guide. thanks in advance.

  2. Avatar
    Doug L Fullerton 5 years ago

    Will it do postscript fonts as well?

    • Avatar Author

      Only tested True Type and Open Type, both of which appeared as True Type. If you manually install one, go into the fonts folder and open it up what does it show as the title in the menu bar?

  3. Avatar
    Doug L Fullerton 5 years ago

    It just shows the font name.  i.e. Akzidenz Grotesk BE Cn

    File properties shows:

    GFBC___.PFM
    Type 1 Font file (.PFM)

    Not sure if that helps or not.  ;P

  4. Avatar
    Patrick 5 years ago

    We currently use the following solution

    set MyDir=\\YourShare\YourFonts
    set MyFonts=*.otf    [or *.ttf]
    set LocalFontDir=%SystemRoot%\Fonts

    for /f %%i in (‘dir /b %MyDir%\%MyFonts%’) do (
    if not exist %LocalFontDir%\%%i cscript.exe %~dp0CopyFont.vbs %MyDir%\%%i
    )

    We are looking for a PowerShell-replacement.

  5. Avatar
    Patrick 5 years ago

    Sorry, obviously the vbs part was missing in my first post:

    Dim MyFont

    If WScript.Arguments.Count > 0 Then MyFont = WScript.Arguments(0) Else WScript.Quit

    ‘Debugging MsgBox “Now Copying ” & MyFont & ” to ” & FONTS

    Const FONTS = &H14&

    Set objShell = CreateObject(“Shell.Application”)
    Set objFolder = objShell.Namespace(FONTS)
    objFolder.CopyHere MyFont

    Set objFolder = nothing
    Set objShell = nothing

  6. Avatar
    Adam 5 years ago

    Broken in W10 1809; Will only install for the Admin user that performs the install, not for all users.

    avatar
    • Avatar
      Nathan 4 years ago

      Did you logoff or restart after the font install? That seems to be necessary in my Windows 10 testing, before the font will show itself to all users.

    • Avatar
      Alon Or 4 years ago

      I use the following code to install Fonts on remote PCs, no need to logoff or reboot. Tested on 1909.

      #Computers to install fonts to
      $ComputerArray = @("Pc1","Pc2","Pc3")
      #A Share containing only the fonts you want to install
      $FontDir="\\FileServer\Shares\Helpdesk\SpecialFonts"
      #Wil be created on remote Pc if not exists, fonts will be copied here and deleted after install.
      $PcStagingDir="c:\tools"
      
      foreach ($pc in $ComputerArray) {
      If((Test-Connection -ComputerName  $pc -Count  1 -ErrorAction SilentlyContinue) -and ($Winrmstatus=Test-WSMan -ComputerName $pc -ErrorAction SilentlyContinue)) {
      $RemotePcStagingDir = "\\$pc\$($PcStagingDir.replace(':','$'))"
      $DisabledSvcs=@()
      $ServiceName = @("WinRM")
      foreach ($svc in $ServiceName) {
              $i=0
              while(((Get-Service -ComputerName $pc -Name $svc -ErrorAction SilentlyContinue).status -ne "Running")-and($i -lt 10)){
                  if((Get-Service -ComputerName $pc -Name $svc -ErrorAction SilentlyContinue).StartType -eq "Disabled"){
                  Write-Host "Try $i , Setting service $svc StartType to Manual on $pc ..."
                  $DisabledSvcs+=$svc
                  Set-Service -ComputerName $pc -Name $svc -StartupType Manual -ErrorAction SilentlyContinue}
              Write-Host "Try $i / 10 , Starting $svc Service on $pc ..."
              $commandz="sc \\"+$pc +" Start "+$svc
              & cmd.exe /c $commandz | Out-Null
              $i++
              sleep 3}
              if($i -ge 10){break}
          }
      if($i -ge 10){Write-Host "Could NOT start service $svc, Skipping Computer $pc" -ForegroundColor Red}else{
      if ( -not (Test-Path -Path $RemotePcStagingDir)){New-Item -Path $RemotePcStagingDir -ItemType Directory -Force}
      $RemoteWinDir=Invoke-Command -ComputerName $pc -ScriptBlock {return $env:windir}
      foreach($FontFile in (Get-ChildItem -file -path $FontDir)){
          if(-not(Test-Path "\\$pc\$($RemoteWinDir.replace(':','$'))\Fonts\$FontFile")){
              Copy-Item "$FontDir\$FontFile" -Destination $RemotePcStagingDir -Force
              Invoke-Command -ComputerName $pc -ScriptBlock {
             $filePath="$using:PcStagingDir\$using:FontFile"
             $fontRegistryPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts"
             $fontsFolderPath = "$($env:windir)\fonts"
          # Create hashtable containing valid font file extensions and text to append to Registry entry name.
          $hashFontFileTypes = @{}
          $hashFontFileTypes.Add(".fon", "")
          $hashFontFileTypes.Add(".fnt", "")
          $hashFontFileTypes.Add(".ttf", " (TrueType)")
          $hashFontFileTypes.Add(".ttc", " (TrueType)")
          $hashFontFileTypes.Add(".otf", " (OpenType)")
          try
          {
              [string]$filePath = (Get-Item $filePath).FullName
              [string]$fileDir  = split-path $filePath
              [string]$fileName = split-path $filePath -leaf
              [string]$fileExt = (Get-Item $filePath).extension
              [string]$fileBaseName = $fileName -replace($fileExt ,"")
      
              $shell = new-object -com shell.application
              $myFolder = $shell.Namespace($fileDir)
              $fileobj = $myFolder.Items().Item($fileName)
              $fontName = $myFolder.GetDetailsOf($fileobj,21)
              
              if ($fontName -eq "") { $fontName = $fileBaseName }
      
              copy-item $filePath -destination $fontsFolderPath
      
              $fontFinalPath = Join-Path $fontsFolderPath $fileName
              if (-not($hashFontFileTypes.ContainsKey($fileExt))){Write-Host "File Extension Unsupported";$retVal = 0}
              if ($retVal -eq 0) {
                  Write-Host "Font `'$($filePath)`'`' installation failed on $env:computername" -ForegroundColor Red
                  Write-Host ""
                  1
              }
              
              else
              {
                  Set-ItemProperty -path "$($fontRegistryPath)" -name "$($fontName)$($hashFontFileTypes.$fileExt)" -value "$($fileName)" -type STRING
                  Write-Host "Font `'$($filePath)`' $fontName $($hashFontFileTypes.$fileExt) installed successfully on $env:computername" -ForegroundColor Green
              }
      
          }
          catch
          {
              Write-Host "An error occured installing `'$($filePath)`' on $env:computername" -ForegroundColor Red
              Write-Host "$($error[0].ToString())" -ForegroundColor Red
              $error.clear()
          }
          }
           }
           Remove-Item "$RemotePcStagingDir\$FontFile" -ErrorAction SilentlyContinue
      }
      }
      foreach($DisabledSvc in $DisabledSvcs){
          Write-Host "Setting service $DisabledSvc StartType to Disabled on $pc ..."
          Set-Service -ComputerName $pc -Name $DisabledSvc -StartupType Disabled -ErrorAction SilentlyContinue
          $commandz="sc \\"+$pc +" Stop "+$DisabledSvc
          & cmd.exe /c $commandz | Out-Null
      }
      }else{if($Winrmstatus){Write-Host "No Connection to Remote Pc $pc" -ForegroundColor Red}Else{Write-Host "No Connection to WinRM service on Remote Pc $pc" -ForegroundColor Red}}
      }

      • Avatar
        sy 4 years ago

        Alan,

        Thanks for sharing. I like your script better than the one in the article as it will pull multiple font types but I'm getting issues remotely connecting to machines. Your script appears to turn on the WinRM service remotely but doesn't seem to work for me in my environment. I'm getting "No Connection to WinRM service on Remote Pc". Any suggestions?

        • Avatar
          Leos Marek (Rank 4) 4 years ago

          Could you share exact error message? Also, did you enable PS remoting on the remote machines with command Enable-PSRemoting?

          • Avatar
            sy 4 years ago

            Leos, thanks for your response. Once enabling that on the remote machine, it worked! I'm assuming PSRemoting should be disabled as it could be a security risk?

            • Avatar
              Leos Marek (Rank 4) 4 years ago

              I dont consider it as a security risk if everything is configured well. PS-Remoting use Kerberos, you can enable it only in Domain profile or specify exact IPs from where you can run the tasks. Both on Windows FW or network FW level.

        • Avatar
          Alon Or 4 years ago
          #Computers to install fonts to
          $ComputerArray = @("Pc1","Pc2","Pc3")
          #A Share containing only the fonts you want to install
          $FontDir="\\FileServer\Shares\Helpdesk\SpecialFonts"
          #Wil be created on remote Pc if not exists, fonts will be copied here and deleted after install.
          $PcStagingDir="c:\tools"
          
          foreach ($pc in $ComputerArray) {
          $RemotePcStagingDir = "\\$pc\$($PcStagingDir.replace(':','$'))"
          If(Test-Connection -ComputerName  $pc -Count  1 -ErrorAction SilentlyContinue) {
          $DisabledSvcs=@()
          $ServiceName = @("WinRM")
          foreach ($svc in $ServiceName) {
                  $i=0
                  while(((Get-Service -ComputerName $pc -Name $svc -ErrorAction SilentlyContinue).status -ne "Running")-and($i -lt 10)){
                      if((Get-Service -ComputerName $pc -Name $svc -ErrorAction SilentlyContinue).StartType -eq "Disabled"){
                      Write-Host "Try $i , Setting service $svc StartType to Manual on $pc ..."
                      $DisabledSvcs+=$svc
                      Set-Service -ComputerName $pc -Name $svc -StartupType Manual -ErrorAction SilentlyContinue}
                  Write-Host "Try $i / 10 , Starting $svc Service on $pc ..."
                  $commandz="sc \\"+$pc +" Start "+$svc
                  & cmd.exe /c $commandz | Out-Null
                  $i++
                  sleep 3}
                  if($i -ge 10){break}
              }
          if($i -ge 10){Write-Host "Could NOT start service $svc, Skipping Computer $pc" -ForegroundColor Red}else{
          New-Item -Path $RemotePcStagingDir -ItemType Directory -Force -ErrorAction SilentlyContinue
          if($RemoteWinDir=Invoke-Command -ComputerName $pc -ScriptBlock {return $env:windir} -ErrorAction SilentlyContinue){
          foreach($FontFile in (Get-ChildItem -file -path $FontDir)){
              if(-not(Test-Path "\\$pc\$($RemoteWinDir.replace(':','$'))\Fonts\$FontFile")){
                  Copy-Item "$FontDir\$FontFile" -Destination $RemotePcStagingDir -Force
                  Invoke-Command -ComputerName $pc -ScriptBlock {
                 $filePath="$using:PcStagingDir\$using:FontFile"
                 $fontRegistryPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts"
                 $fontsFolderPath = "$($env:windir)\fonts"
              # Create hashtable containing valid font file extensions and text to append to Registry entry name.
              $hashFontFileTypes = @{}
              $hashFontFileTypes.Add(".fon", "")
              $hashFontFileTypes.Add(".fnt", "")
              $hashFontFileTypes.Add(".ttf", " (TrueType)")
              $hashFontFileTypes.Add(".ttc", " (TrueType)")
              $hashFontFileTypes.Add(".otf", " (OpenType)")
              try
              {
                  [string]$filePath = (Get-Item $filePath).FullName
                  [string]$fileDir  = split-path $filePath
                  [string]$fileName = split-path $filePath -leaf
                  [string]$fileExt = (Get-Item $filePath).extension
                  [string]$fileBaseName = $fileName -replace($fileExt ,"")
          
                  $shell = new-object -com shell.application
                  $myFolder = $shell.Namespace($fileDir)
                  $fileobj = $myFolder.Items().Item($fileName)
                  $fontName = $myFolder.GetDetailsOf($fileobj,21)
                  
                  if ($fontName -eq "") { $fontName = $fileBaseName }
          
                  copy-item $filePath -destination $fontsFolderPath
          
                  $fontFinalPath = Join-Path $fontsFolderPath $fileName
                  if (-not($hashFontFileTypes.ContainsKey($fileExt))){Write-Host "File Extension Unsupported";$retVal = 0}
                  if ($retVal -eq 0) {
                      Write-Host "Font `'$($filePath)`'`' installation failed on $env:computername" -ForegroundColor Red
                      Write-Host ""
                      1
                  }
                  
                  else
                  {
                      Set-ItemProperty -path "$($fontRegistryPath)" -name "$($fontName)$($hashFontFileTypes.$fileExt)" -value "$($fileName)" -type STRING
                      Write-Host "Font `'$($filePath)`' $fontName $($hashFontFileTypes.$fileExt) installed successfully on $env:computername" -ForegroundColor Green
                  }
          
              }
              catch
              {
                  Write-Host "An error occured installing `'$($filePath)`' on $env:computername" -ForegroundColor Red
                  Write-Host "$($error[0].ToString())" -ForegroundColor Red
                  $error.clear()
              }
              }
               }
               Remove-Item "$RemotePcStagingDir\$FontFile" -ErrorAction SilentlyContinue
          }
          Write-Host "Done working on Remote Pc $pc" -ForegroundColor Green
          }else{Write-Host "Could not Invoke-Command to Remote Pc $pc" -ForegroundColor Red}
          }
          foreach($DisabledSvc in $DisabledSvcs){
              Write-Host "Setting service $DisabledSvc StartType to Disabled on $pc ..."
              Set-Service -ComputerName $pc -Name $DisabledSvc -StartupType Disabled -ErrorAction SilentlyContinue
              $commandz="sc \\"+$pc +" Stop "+$DisabledSvc
              & cmd.exe /c $commandz | Out-Null
          }
          }Else{Write-Host "No Connection to WinRM service on Remote Pc $pc" -ForegroundColor Red}
          }

           

          • Avatar
            Leos Marek (Rank 4) 4 years ago

            Why are you using such script to start WinRM service? It is very unusual. Remember that the *-Service commands use WMI to run, which might be blocked on firewalls. Standard procedure is to have Powershell remoting enabled on all computers via GPO and then just use Invoke-command or other commands that run via WinRM.

            • Avatar
              Alon Or 4 years ago

              Let`s say you just got there and have no time to start making other adjustments, GPO`s and other things.

              As for the FW thing, I prefer to have a VM for administration purposes so all the ports needed are open.

              My script simply works , dirty, but works, for sure I could make it better , but for now it just fill`s the need.

              Feel free to make it better and share it . Thank You.

              • Avatar
                Leos Marek (Rank 4) 4 years ago

                If you had time to configure FW for your specific management VM, then you also got time to enable Ps-Remoting which is one time action 🙂

                You can work as you wish, of course.

                • Avatar
                  Alon Or 4 years ago

                  The guy before me had the VM  and FW configured wink

      • Avatar
        mikeg 3 years ago

        If I’m not mistaken, in 1909 it is already possible to install fonts on behalf of a regular user. (right-click), i.e. if such a concept is allowed in the organization, this reduces the load of requests for the IT department.

  7. Avatar
    Ian Manning 5 years ago

    Incredible help as part of a larger install script I wrote today, thanks so much!

    • Avatar
      Roy Hill 4 years ago

      Ian Manning, would you be willing to share your script with us?

  8. Avatar
    Mickael 5 years ago

    Thx a lot for your help with this script. I just have one question.
    Why he stop processing at the “conneting to” and don’t go to “Installing Font”

  9. Avatar
    Prabhas 4 years ago

    Hi Rob, any solution for Windows 10 1809 ? Not sure if you have any working script for install fonts on Windows 10 1809.

    Thanks in advance.

  10. Avatar
    Christian Barnes 4 years ago

    Has anyone managed to make this work through Powershell since 1809 was released? The copyhere method  I used to use is not working through Intune.

    • Avatar
      Christian 4 years ago

      Just to mention, in the end I wrote a basic script that used 'copy-item' for each font straight into the system fonts folder and added a registry key into the HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts. I put this in a for loop to install any .ttf fonts found in a certain folder and it worked when deployed through Intune in a cloud only environment.

  11. Avatar
    chungb 4 years ago

    Hi, when running the script I keep receive the error " Computer unavailable" but I able to ping the computer

  12. Avatar
    mikeg 3 years ago

    I also want to share a primitive task of installing fonts using Ansible:


    – name: Install Fonts
    hosts: all
    tasks:
    – name: FontsTestPS
    win_shell: |
    $fonts = (New-Object -ComObject Shell.Application).Namespace(0x14)
    dir \\someserver\FONTS\*.ttf | %{ $fonts.CopyHere($_.fullname) }
    dir \\someserver\FONTS\*.otf | %{ $fonts.CopyHere($_.fullname) }

    – name: Warn logged in users of Installed Fonts
    win_msg:
    display_seconds: 20
    msg: The fonts are installed, click OK. if necessary, restart the application.

  13. Avatar
    Michael Cannard 3 years ago

    Here is a simpler powershell solution that accounts for User installed fonts. Tested in Windows 10 20H2.

    Mostly stolen from other posts though – Just updated a bit.

     

    $Path="c:\temp\fonts"
    $Fontdir = dir $Path
    $FONTS = 0x14
    $objShell = New-Object -ComObject Shell.Application
    $objFolder = $objShell.Namespace($FONTS)
    $FontUser = $env:LOCALAPPDATA + "\Microsoft\Windows\Fonts"
    foreach($File in $Fontdir) 
    	{
    	if(($file.name -match "TTF") -or ($file.name -match "TTC") -or ($file.name -match "OTF"))
    		{
    		$try = $true
    		$installedFonts1 = @(Get-ChildItem c:\windows\fonts | Where-Object {$_.PSIsContainer -eq $false} | Select-Object basename)
            $installedFonts2 = @(Get-ChildItem $FontUser | Where-Object {$_.PSIsContainer -eq $false} | Select-Object basename)
            $installedFonts = $installedfonts1 + $installedfonts2
    		$name = $File.baseName
    		foreach($font in $installedFonts)
    			{
    			$font = $font -replace "_", ""
    			$name = $name -replace "_", ""
    			if($font -match $name)
    				{
    				$try = $false
    				}
    			}
    		if($try)
    			{
    			$objFolder.CopyHere($File.fullname)
            	}
    		}
    	}

  14. Avatar

    This should be more efficient

    
    $Path = Get-ChildItem -Path 'c:\temp\fonts' -File 
    
    $objShell = New-Object -ComObject Shell.Application
    
    $FontFolder = $objShell.NameSpace([environment+specialfolder]::Fonts)
    
    $UserFontPath = [system.io.Path]::Combine($env:LOCALAPPDATA, 'Microsoft\Windows\Fonts')
    
    $InstalledFontList = [system.collections.generic.list[string]]::new()
    
    $InstalledFontList.AddRange( @(Get-ChildItem -Path $Path -File | Select-Object -ExpandProperty baseName), 
    
                                 @(Get-ChildItem -Path $UserFontPath -File | Select-Object -ExpandProperty baseName))
    
    foreach ($file in $Path) {
    
        $FontTestName = $file.basename.replace('_', '')
    
        if ($file.extension -match '\.(TTF|TTC|OTF)$' -and $file.basename -match $FontTestName) {
    
            if ($InstalledFontList -contains $NewFontName) {
    
                write-verbose -Message ('Font ({0}) already installed' -f $NewFontName)
    
            } 
    
            else {
    
                $FontFolder.CopyHere($file)
    
            }
    
        }
    
    }
    
    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($FontFolder)
    
    

  15. Avatar
    David Francis 2 years ago

    Excellent! The Font-Install.ps1 script worked flawlessly.
    Much appreciated!

Leave a reply to Patrick Click here to cancel the reply

Please enclose code in pre tags: <pre></pre>

Your email address will not be published. Required fields are marked *

*

© 4sysops 2006 - 2023

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account