I am currently writing my first script in Powershell and I am already facing the first problem.
I would like to read the value from a variable in a function so that I can use this variable in another cmd-let later. The problem now is that the variable is only recognized inside the function block and not outside.
How do I get this to work?
Thanks for the help :-)
function Write-Log([string]$logtext, [int]$level=0)
{
if($level -eq 0)
{
$logtext = "[INFO] " + $logtext
$text = "["+$logdate+"] - " + $logtext
Write-Host $text
}
}
Send-MailMessage -To "<xxx#xxx.de>" -Subject "$text" -Body "The GPO backup creation was completed with the following status: `n $text" -SmtpServer "xxx#xxx.de" -From "xxx#xxx.de"
I would like to submit $text
This has to do with variable scoping behavior in PowerShell.
By default, all variables in the caller's scope is visible inside the function. So we can do:
function Print-X
{
Write-Host $X
}
$X = 123
Print-X # prints 123
$X = 456
Print-X # prints 456
So far, so good. But when we start writing to variables outside the function itself, PowerShell transparently creates a new variable inside the function's own scope:
function Print-X2
{
Write-Host $X # will resolve the value of `$X` from outside the function
$X = 999 # This creates a new `$X`, different from the one outside
Write-Host $X # will resolve the value of the new `$X` that new exists inside the function
}
$X = 123
Print-X2 # Prints 123, and 999
Write-Host $X # But the value of `$X` outside is still 123, unchanged
So, what to do? You could use a scope modifier to write to the variable outside the function, but the real solution here is to return the value from the function instead:
function Write-Log([string]$logtext, [int]$level=0, [switch]$PassThru = $true)
{
if($level -eq 0)
{
$logtext = "[INFO] " + $logtext
$text = "["+$logdate+"] - " + $logtext
Write-Host $text
if($PassThru){
return $text
}
}
}
$logLine = Write-Log "Some log message" -PassThru
Send-MailMessage -Subject $logLine ...
if you need to access a variable outside a function in Powershell you might need to use the global variable.
$global:myglobalvariable="This is a PowerShell global variable"
or if its a null
$global:myglobalvariable2 = $null
Im doing an script which automates the creation of several things on some firewalls (Sophos XG). This one reads a CSV with 2 column and creates an API call to create IP Host in the firewall with name (first column) and IP (second column). Ideal for massive uploads.
Its works fine, except when some variables have special characters (for example + or &) either in the password variable ($_CONTRASEÑA_API) or the IP's name ($_NOMBRE_IP). If the password or the name doesn't have those chars, it works flawlessly.
Here's the code:
# ---------------------
# Creacion IP Host - XG
# ---------------------
# Versiones
#
# 1.0 - Version original
#
# ---------------------
# Declaracion variables globales
# ------------------------------
$_IP_FIREWALL = "190.80.80.80"
$_PUERTO_FIREWALL = "4444"
$_USUARIO_API = "admin"
$_CONTRASEÑA_API = "SecurePassword+"
$_RUTA_TRABAJO = "c:\Workfolder\IPHostCSV\"
$_NOMBRE_ARCHIVO_CSV = "test.csv"
# Permitir conexiones SSL
# -----------------------
add-type #"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
"#
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
# Bucle principal
# ---------------
foreach($linea in [System.IO.File]::ReadLines("$_RUTA_TRABAJO$_NOMBRE_ARCHIVO_CSV"))
{
# Obtencion Parametros CSV
# ------------------------
$_NOMBRE_IP = ($linea -split ',')[0]
$_DIRECCION_IP = ($linea -split ',')[1]
$_MASCARA = ($linea -split ',')[2]
# Consulta API
# ------------
echo "[INFO] Procesando Host: $($_NOMBRE_IP) IP: $($_DIRECCION_IP)"
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$_URL_API = "https://$($_IP_FIREWALL):$($_PUERTO_FIREWALL)/webconsole/APIController?reqxml=<Request><Login><UserName>$($_USUARIO_API)</UserName><Password>$($_CONTRASEÑA_API)</Password></Login><Set><IPHost><Name>$($_NOMBRE_IP)</Name><IPFamily>IPv4</IPFamily><HostType>IP</HostType><IPAddress>$($_DIRECCION_IP)</IPAddress></IPHost></Set></Request>"
$_RESULTADO_API = Invoke-WebRequest -Uri "$_URL_API"
[xml] $_CODIGO_XML = $_RESULTADO_API.Content
# Revision Resultados
# -------------------
if($_CODIGO_XML.Response.IPHost.Status.code -eq 200)
{
echo "[ OK ] CODIGO: $($_CODIGO_XML.Response.IPHost.Status.code) - Procesado OK"
$_NOMBRE_IP | Out-File -Append -Encoding utf8 -FilePath "$($_RUTA_TRABAJO)OK.txt"
} else {
echo "[WARN] CODIGO: $($_CODIGO_XML.Response.IPHost.Status.code) - Problema en la ejecuccion $($_CODIGO_XML.Response.IPHost.Status.'#text')"
$_NOMBRE_IP | Out-File -Append -Encoding utf8 -FilePath "$($_RUTA_TRABAJO)NO_OK.txt"
}
}
Basically, these 2 lines
$_URL_API = "https://$($_IP_FIREWALL):$($_PUERTO_FIREWALL)/webconsole/APIController?reqxml=<Request><Login><UserName>$($_USUARIO_API)</UserName><Password>$($_CONTRASEÑA_API)</Password></Login><Set><IPHost><Name>$($_NOMBRE_IP)</Name><IPFamily>IPv4</IPFamily><HostType>IP</HostType><IPAddress>$($_DIRECCION_IP)</IPAddress></IPHost></Set></Request>"
$_RESULTADO_API = Invoke-WebRequest -Uri "$_URL_API"
[xml] $_CODIGO_XML = $_RESULTADO_API.Content
Do the call. Any idea how to avoid the error when specials chars are used either in password or any other place in the URI?
Thanks!
Antonio.
(Sorry for the English, not my mother tongue).
User the UrlEncode method of HttpUtility. This will convert special character to their url friendly alternative.
$_URL_API_Encoded = [System.Web.HttpUtility]::UrlEncode($_URL_API)
$_RESULTADO_API = Invoke-WebRequest -Uri "$_URL_API_Encoded"
I need to change the local administrator name and password on servers to those that are contained in a .csv
The CSV file contains a list with all the information in it whereby the Server, Administrator name and Passwords are different on each line
The csv is headed by three columns - Server,Admin,PW
How could this be done using Powershell?
I know i can set them all the same using this but they need to be as per each csv line.
foreach ($strComputer in get-content c:\Servers.txt)
{
$Admin=[adsi]("WinNT://" + $strComputer + "/Administrator, user")
$Admin.psbase.rename("Newname")
$Admin.SetPassword("NewPW")
try this ( not tested ):
import-csv c:\servers.txt | % {
$Admin=[adsi]("WinNT://" + $($_.Server) + "/Administrator, user")
$Admin.psbase.rename($($_.Admin))
$Admin.SetPassword($($_.PW))
$Admin.SetInfo() # I think it's needed
}
you can use the Import-Csv instead of get-content. then you can adress the variables by using the header names.
asuming you have a file like:
Server,Admin,PW
bla1,bla2,bla3
blaA,blaB,blaC
the output of
foreach ($line in Import-Csv c:\Servers.txt) { echo $line.server }
would be:
bla1
blaA
just to complete your code, try this example:
foreach ($line in Import-Csv c:\Servers.txt)
{
$Admin=[adsi]("WinNT://" + $line.Server + "/Administrator, user")
$Admin.psbase.rename($line.Admin)
$Admin.SetPassword($line.PW)
}
I've got a CSV-file from HR with aprox 1000 lines (employees) that I feed to AD with Powershell.
This works, but I am a bit uncertain if I am doing this the right way.
This are my major concerns:
I am setting the attributes one at a time. Should I put the "changes" into an some kind of array/hasthable/object and do it all at once at the end of the script? But How? "New-Object"?
Should I use functions? But how can I return values (and continue based on the result from the function)?
All programming hints, corrections would be GREATLY appreciated. I really understand this wonderful community of knowledgable people so, let me have it. If you have the time please tell me how I can do this better..
This is my code:
Add-PSSnapin Microsoft.Exchange.Management.PowerShell.Admin -ErrorAction silentlycontinue
Add-PSSnapin quest.activeroles.admanagement -ErrorAction silentlycontinue
$file = "\Scripts\employees.csv" # Location of the input file
$file2 = "\Scripts\employees2.csv" # Temp file
$logfile = "\Scripts\logfile.txt" # log file
remove-item $logfile -Force -ErrorAction SilentlyContinue
Get-Content $file | Out-File -Encoding UTF8 $file2 # Convert to UTF8 (we don't touch the original inputfile)
$ListEmployees = Import-Csv $file2 -Delimiter ";" # Import the file to CSV
foreach ($ListEmployee in $ListEmployees) {
$ListDisplayName = $ListEmployee.firstname + " " + $ListEmployee.lastname
if($ADemployee = Get-QADUser -displayname $ListDisplayName -IncludedProperties employeeid )
{
## CHECK NAME
if($($ADEmployee.displayname) -eq $($ListDisplayName))
{
echo "MATCH: $($ADEmployee.displayname)"
}
## CHECK COMPANY
if($($ADEmployee.company) -ne $($ListEmployee.company))
{
echo " CHANGE - Company: '$($ADEmployee.company)' to '$($ListEmployee.company)'"
Set-QADUser -identity $($ADEmployee.samaccountname) -Company $($ListEmployee.company) -WhatIf
}
else
{
echo " OK - Company : no change '$($ListEmployee.company)'"
}
## CHECK OFFICE
if($($ADEmployee.office) -ne $($ListEmployee.office))
{
echo " CHANGE - Office '$($ADEmployee.office)' to '$($ListEmployee.office)'"
Set-QADUser -identity $($ADEmployee.samaccountname) -Office $($ListEmployee.Office) -WhatIf
}
else
{
echo " OK - Office : no change '$($ListEmployee.office)'"
}
## CHECK MOBILE
if( $listemployee.mobile -match '\S' )
{
if($($ADEmployee.mobile) -ne $($ListEmployee.mobile))
{
echo " CHANGE - Mobile : '$($ADEmployee.mobile)' to '$($ListEmployee.mobile)'"
Set-QADUser -identity $($ADEmployee.samaccountname) -Mobile $($ListEmployee.mobile) -WhatIf
}
else
{
echo " OK - Mobile : no change '$($ListEmployee.mobile)'"
}
}
## CHECK EMPLOYEEID
if($($ADEmployee.employeeid) -ne $($ListEmployee.employeeid))
{
echo " CHANGE - EmployeeID: '$($ADEmployee.employeeid)' to '$($ListEmployee.employeeid)'"
Set-QADUser -identity $($ADEmployee.samaccountname) -ObjectAttributes #{employeeID = $($ListEmployee.employeeid)} -WhatIf
}
else
{
echo " OK - EmployeeID : no change '$($ListEmployee.employeeid)'"
}
$match++
}
else
{
if($EXContact = Get-Contact $ListDisplayName -ErrorAction SilentlyContinue)
{
echo "MATCH CONTACT: $ListDisplayName (contact)"
## CHECK MOBILE
if( $listemployee.mobile -match '\S' )
{
if($($EXContact.Mobilephone) -ne $($ListEmployee.mobile))
{
echo " CHANGE - Mobile : '$($EXContact.Mobilephone)' to '$($ListEmployee.mobile)'"
}
else
{
echo " OK - Mobile ; No change ($($ListEmployee.mobile))"
}
}
## CHECK COMPANY
if($($EXContact.company) -ne $($ListEmployee.company))
{
echo " CHANGE - Company: '$($EXContact.company)' to '$($ListEmployee.company)'"
}
else
{
echo " OK - Company : No change($($ListEmployee.company))"
}
## CHECK OFFICE
if($($EXContact.office) -ne $($ListEmployee.office))
{
echo " CHANGE - Office '$($EXContact.office)' to '$($ListEmployee.office)'"
}
else
{
echo " OK - Office : No Change($($ListEmployee.office))"
}
$contactmatch++
}
else
{
echo "$ListDisplayName" | Out-File $logfile -Append
echo "NO MATCH: $ListDisplayName"
$nomatch++
}
}
$i++
}
echo " "
echo "List contains $i accounts"
echo "Accounts: $match matches"
echo "Contacts: $contactmatch"
echo "No Match: $nomatch"
And; If you think this is cr*p, tell me! I'd rather hear it from you than you staying silent just to be polite! I am "quite" new to this so I deserve it:)
Something that seems odd about the whole thing is using display name as your identity reference. As an identity reference, it't both volatile and potentially ambiguos in AD, and seems a poor choice to use to drive a maintenance script.
Here is my opinion :
1) I really think that the problem #mjolinor point is important, and you will meet troubles (I mean need human check) if you don't use one of the identity attributes fixed by Microsoft (samAccountName, userPrincipalName or better objectGuid, objectSid ...) as a key to find your users in Active-Directory.
If it's not possible you perhaps can buid a filter on the top of multiples attributes. If you CSV comes from another LDAP Directory you perhaps can integrate their unique ID in you Schema (in this case see Microsoft Services for UNIX 3.5 (MSFU3.5) schema extensions to Active Directory).
2) Once you find one of your CSV entry in your Active-Directory, you check each attributes, and then replace 'one by one' the ones in your AD with the one in your CSV.
Here my advice will be to check all the differencies between your CSV and AD entry, and them made an unique change into the Directory. In fact, on one différence, I will change them all in one command. I don't know how Set-QADUser is written, but in the low level layers all the attributes replacement can be made one shot (LDAP_REPLACE, or in a single ADSI commit)
3) Just a remark : begining PowerShell V2 (Seven, W2K8) an Active-Directory module is given by Microsoft.
Is there a way to programmatically install a certificate into mozilla? We're trying to script everything to eliminate deviations in environment so installing it by hand through mozilla preferences does not work for our needs.
I assume theres a way to do it with certutil, but I am not sure of Mozilla's internals, etc.
Here is an alternative way that doesn't override the existing certificates:
[bash fragment for linux systems]
certificateFile="MyCa.cert.pem"
certificateName="MyCA Name"
for certDB in $(find ~/.mozilla* ~/.thunderbird -name "cert8.db")
do
certDir=$(dirname ${certDB});
#log "mozilla certificate" "install '${certificateName}' in ${certDir}"
certutil -A -n "${certificateName}" -t "TCu,Cuw,Tuw" -i ${certificateFile} -d ${certDir}
done
You may find certutil in the libnss3-tools package (debian/ubuntu).
Source:
http://web.archive.org/web/20150622023251/http://www.computer42.org:80/xwiki-static/exported/DevNotes/xwiki.DevNotes.Firefox.html
See also:
https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/tools/NSS_Tools_certutil
The easiest way is to import the certificate into a sample firefox-profile and then copy the cert8.db to the users you want equip with the certificate.
First import the certificate by hand into the firefox profile of the sample-user. Then copy
/home/${USER}/.mozilla/firefox/${randomalphanum}.default/cert8.db (Linux/Unix)
%userprofile%\Application Data\Mozilla\Firefox\Profiles\%randomalphanum%.default\cert8.db (Windows)
into the users firefox-profiles. That's it. If you want to make sure, that new users get the certificate automatically, copy cert8.db to:
/etc/firefox-3.0/profile (Linux/Unix)
%programfiles%\firefox-installation-folder\defaults\profile (Windows)
Firefox now (since 58) uses a SQLite database cert9.db instead of legacy cert8.db.
I have made a fix to a solution presented here to make it work with new versions of Firefox:
certificateFile="MyCa.cert.pem"
certificateName="MyCA Name"
for certDB in $(find ~/.mozilla* ~/.thunderbird -name "cert9.db")
do
certDir=$(dirname ${certDB});
#log "mozilla certificate" "install '${certificateName}' in ${certDir}"
certutil -A -n "${certificateName}" -t "TCu,Cuw,Tuw" -i ${certificateFile} -d sql:${certDir}
done
Just wanted to add to an old thread to hopefully aid other people. I needed programmatically add a cert to the firefox database using a GPO, this was how I did it for Windows
1, First download and unzip the precompiled firefox NSS nss-3.13.5-nspr-4.9.1-compiled-x86.zip
2, Add the cert manually to firefox Options-->Advanced--Certificates-->Authorities-->Import
3, from the downloaded NSS package, run
certutil -L -d c:\users\[username]\appdata\roaming\mozilla\firefox\[profile].default
4, The above query will show you the certificate name and Trust Attributes e.g.
my company Ltd CT,C,C
5, Delete the certificate in step 2. Options-->Advanced--Certificates-->Authorities-->Delete
6, Create a powershell script using the information from step 4 as follows. This script will get the users profile path and add the certificate. This only works if the user has one firefox profile (need somehow to retrieve the users firefox folder profile name)
#Script adds Radius Certificate to independent Firefox certificate store since the browser does not use the Windows built in certificate store
#Get Firefox profile cert8.db file from users windows profile path
$ProfilePath = "C:\Users\" + $env:username + "\AppData\Roaming\Mozilla\Firefox\Profiles\"
$ProfilePath = $ProfilePath + (Get-ChildItem $ProfilePath | ForEach-Object { $_.Name }).ToString()
#Update firefox cert8.db file with Radius Certificate
certutil -A -n "UK my company" -t "CT,C,C" -i CertNameToAdd.crt -d $ProfilePath
7, Create GPO as a User Configuration to run the PowerShell script
Hope that helps save someone time
Recent versions of Firefox support a policies.json file that will be applied to all Firefox profiles.
For CA certificates, you have some options, here's one example, tested with Linux/Ubuntu where I already have system-wide CA certs in /usr/local/share/ca-certificates:
In /usr/lib/firefox/distribution/policies.json
{
"policies": {
"Certificates": {
"Install": [
"/usr/local/share/ca-certificates/my-custom-root-ca.crt"
]
}
}
}
Support for Thunderbird is on its way.
On Windows 7 with Firefox 10, the cert8.db file is stored at %userprofile%\AppData\Roaming\Mozilla\Firefox\Profiles\########.default\cert8.db. If you are an administrator, you can probably write a simple WMI application to copy the file to the User's respective folder.
Also, a solution that worked for me from http://www.appdeploy.com/messageboards/tm.asp?m=52532&mpage=1&key=촴
Copied CERTUTIL.EXE from the NSS zip file ( http://www.mozilla.org/projects/security/pki/nss/tools/ ) to C:\Temp\CertImport (I also placed the certificates I want to import there)
Copied all the dll's from the NSS zip file to C\:Windows\System32
Created a BAT file in %Appdata%\mozilla\firefox\profiles with this script...
Set FFProfdir=%Appdata%\mozilla\firefox\profiles
Set CERTDIR=C:\Temp\CertImport
DIR /A:D /B > "%Temp%\FFProfile.txt"
FOR /F "tokens=*" %%i in (%Temp%\FFProfile.txt) do (
CD /d "%FFProfDir%\%%i"
COPY cert8.db cert8.db.orig /y
For %%x in ("%CertDir%\Cert1.crt") do "%Certdir%\certutil.exe" -A -n "Cert1" -i "%%x" -t "TCu,TCu,TCu" -d .
For %%x in ("%CertDir%\Cert2.crt") do "%Certdir%\certutil.exe" -A -n "Cert2" -i "%%x" -t "TCu,TCu,TCu" -d .
)
DEL /f /q "%Temp%\FFProfile.txt"
Executed the BAT file with good results.
I was trying to achieve the same thing in Powershell and wrote a script to perform various functions that can be interactively selected. Of course, it's fairly easy to modify the script to automate certain things instead of provide options.
I'm an Infrastructure guy rather than a coder/programmer, so apologies if it's a bit cumbersome (but it does work!!).
Save the following as a PS1:
##################################################################################################
#
# NAME: RegisterFireFoxCertificates.ps1
#
# AUTHOR: Andy Pyne
#
# DATE : 22.07.2015
#
# COMMENT: To provide options for listing, adding, deleting and purging
# FireFox Certificates using Mozilla's NSS Util CertUtil
# Source: https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/tools/NSS_Tools_certutil
#
# NOTE: You need a copy of the NSS Util CertUtil and it's associated dll's
# The specific files I used were:
#
# certutil.exe, fort32.dll, freebl3.dll, libnspr4.dll, libplc4.dll, libplds4.dll, nspr4.dll,
# nss3.dll, nssckbi.dll, nssdbm3.dll, nssutil3.dll, plc4.dll, plds4.dll, smime3.dll,
# softokn3.dll, sqlite3.dll, ssl3.dll, swft32.dll
#
##################################################################################################
##################################################################################################
# Setup a few parameters
$ErrorActionPreference = "Silentlycontinue"
$ExecutionPolicyOriginal = Get-ExecutionPolicy
$FireFoxExecutable = "C:\Program Files (x86)\Mozilla Firefox\Firefox.exe"
# This is the Firefox certificate database
$CertDB = "Cert8.db"
# The Certificate Nickname is a name you want to see on the certificates that you've imported in - so you know they were imported by this process
# However, when you look at the certificates in Firefox, they will be listed under whatever the certificate name was when it was generated
# So if your certificate is listed as 'Company123' when imported, it will still be called that as the Common Name, but when you click to view
# it, you will see that the first item in the Certificate Fields is what you 'nicknamed' it.
$CertificateNickname = "MyCompanyName FF AutoImport Cert"
# The Legacy Certificates are specific/explicit certificates which you wish to delete (The 'purge' option later in the script references these items)
$LegacyCertificates = #("OldCertificate1", "Company Cert XYZ", "Previous Company name", "Unwanted Certificate - 7", "123APTEST123")
# This is the list of databases / Firefox profiles on the machine
$FFDBList = #()
# Making sure our temporary directory is empty
$FFCertLocationLocal = "C:\FFCertTemp"
# The remote location of the certificates and
$FFCertLocationRemote = "\\myUNC\NETLOGON\FireFoxCert\"
# The local CertUtil executable (this is copied from the remote location above)
$FFCertTool = "$FFCertLocationLocal\CertUtil.exe"
# Making sure our temporary directory is empty
Remove-Item $FFCertLocationLocal -Recurse
New-Item -ItemType Directory -Path $FFCertLocationLocal
##################################################################################################
##################################################################################################
Clear
# We're going to get a list of the Firefox processes on the machine that are open and close them
# Otherwise the add/delete parts might not be successful with Firefox still running
$FireFoxRunningProcessesList = Get-Process | Where-Object {$_.Name -Match "FireFox"} | Select-Object ProcessName,Id | Format-Table -AutoSize
$FireFoxRunningProcesses = Get-Process | Where-Object {$_.Name -Match "FireFox"} | Select-Object -ExpandProperty Id
If (!$FireFoxRunningProcesses) {}
Else {
Write-Host "The following processes will be stopped to perform certificate manipulation:"
$FireFoxRunningProcessesList
$TerminateProcessQuestion = Read-Host "To auto-terminate (ungracefully!) processes, press 'Y', otherwise, press any other key"
If ($TerminateProcessQuestion -ne 'y') {
Clear
Write-Host "Cannot continue as Firefox process is still running, ending script ..."
Exit}
Else {ForEach ($FireFoxRunningProcess in $FireFoxRunningProcesses) {
[Int]$FireFoxRunningProcess = [Convert]::ToInt32($FireFoxRunningProcess, 10)
Stop-Process -Id $FireFoxRunningProcess -Force}}
}
##################################################################################################
##################################################################################################
# The remote files (certificates and the NSS Tools CertUtil files are copied locally)
$FFCertificateListItemRemote = Get-ChildItem $FFCertLocationRemote -Recurse -Include *.cer,*.dll,certutil.exe
ForEach ($FFCertificateItemRemote in $FFCertificateListItemRemote) {
Copy-Item $FFCertificateItemRemote.FullName -Destination $FFCertLocationLocal}
# Get a list of the local certificates
$FFCertificateListLocal = Get-ChildItem $FFCertLocationLocal -Recurse -filter *.cer
Clear
Set-ExecutionPolicy "Unrestricted"
# Find all Firefox profiles and create an array called FFDBList
# Of course, you'll only be able to get to the ones your permissions allow
$LocalProfiles = Get-ChildItem "C:\Users" | Select-Object -ExpandProperty FullName
ForEach ($LocalProfile in $LocalProfiles) {
$FFProfile = Get-ChildItem "$LocalProfile\AppData\Roaming\Mozilla\Firefox\Profiles" | Select-Object -ExpandProperty FullName
If (!$FFProfile) {Write-Host "There is no Firefox Profile for $LocalProfile"}
ELSE {$FFDBList += $FFProfile}
}
Clear
Write-Host "#################################"
Write-Host "The List of FireFox Profiles is:"
Write-Host "#################################"
$FFDBList
PAUSE
##################################################################################################
##################################################################################################
# Setup 4x functions (List, Delete, Add and Purge)
#
# - List will simply list certificates from the Firefox profiles
#
# - Delete will delete the certificates the same as the certificates you're going to add back in
# So for example, if you have 2x certificates copied earlier for import, 'CompanyA' and 'CompanyZ'
# then you can delete certificates with these names beforehand. This will prevent the
# certificates you want to import being skipped/duplicated because they already exist
#
# - Add will simply add the list of certificates you've copied locally
#
# - Purge will allow you to delete 'other' certificates that you've manually listed in the
# variable '$LegacyCertificates' at the top of the script
# Each of the functions perform the same 4x basic steps
#
# 1) Do the following 3x things for each of the Firefox profiles
# 2) Do the 2x following things for each of the certificates
# 3) Generate an expression using parameters based on the certificate nickname specified
# earlier, and the profile and certificate informaiton
# 4) Invoke the expression
Function ListCertificates {
Write-Host "#############################"
ForEach ($FFDBItem in $FFDBList) {
$FFCertificateListItemFull = $FFCertificateListItem.FullName
Write-Host "Listing Certificates for $FFDBitem"
$ExpressionToListCerts = "$FFCertTool -L -d `"$FFDBItem`""
Invoke-Expression $ExpressionToListCerts
}
PAUSE}
Function DeleteOldCertificates {
Write-Host "#############################"
ForEach ($FFDBItem in $FFDBList) {
ForEach ($FFCertificateListItem in $FFCertificateListLocal) {
$FFCertificateListItemFull = $FFCertificateListItem.FullName
Write-Host "Deleting Cert $FFCertificateListItem for $FFDBitem"
$ExpressionToDeleteCerts = "$FFCertTool -D -n `"$CertificateNickname`" -d `"$FFDBItem`""
Invoke-Expression $ExpressionToDeleteCerts
}}
PAUSE}
Function AddCertificates {
Write-Host "#############################"
ForEach ($FFDBItem in $FFDBList) {
ForEach ($FFCertificateListItem in $FFCertificateListLocal) {
$FFCertificateListItemFull = $FFCertificateListItem.FullName
Write-Host "Adding $FFCertificateListItem Cert for $FFDBitem"
$ExpressionToAddCerts = "$FFCertTool -A -n `"$CertificateNickname`" -t `"CT,C,C`" -d `"$FFDBItem`" -i `"$FFCertificateListItemFull`""
Write-Host $ExpressionToAddCerts
Invoke-Expression $ExpressionToAddCerts
#PAUSE
}}
PAUSE}
Function PurgeLegacyCertificates {
Write-Host "#############################"
ForEach ($FFDBItem in $FFDBList) {
ForEach ($LegacyCertificateItem in $LegacyCertificates) {
$LegacyCertificateItemFull = $LegacyCertificateItem.FullName
Write-Host "Purging Old Certs ($LegacyCertificateItem) for $FFDBitem"
#$ExpressionToDeleteLegacyCerts = "$FFCertTool -D -n `"$OldCertificate`" -d `"$FFDBItem`""
$ExpressionToDeleteLegacyCerts = "$FFCertTool -D -n `"$LegacyCertificateItem`" -d `"$FFDBItem`""
ForEach ($LegacyCertificate in $LegacyCertificates) {
Invoke-Expression $ExpressionToDeleteLegacyCerts}
}}
PAUSE}
##################################################################################################
##################################################################################################
# Creating a few options to invoke the various functions created above
$CertificateAction = ""
Function CertificateActionSelection {
Do {
Clear
$CertificateAction = Read-Host "Would you like to [L]ist all certificates [D]elete all old certificates, [A]dd new certificates, or [P]urge legacy certificates?"
} Until ($CertificateAction -eq "L" -or $CertificateAction -eq "D" -or $CertificateAction -eq "A" -or $CertificateAction -eq "P" )
If ($CertificateAction -eq "L") {ListCertificates}
If ($CertificateAction -eq "D") {DeleteOldCertificates}
If ($CertificateAction -eq "A") {AddCertificates}
If ($CertificateAction -eq "P") {PurgeLegacyCertificates}
}
Do {
Clear
$MoreCertificateActions = Read-Host "Would you like to [L]aunch Firefox (as $env:USERNAME), take a [C]ertificate action, or [Q]uit?"
If ($MoreCertificateActions -eq "L") {
Invoke-Item $FireFoxExecutable
Exit}
If ($MoreCertificateActions -eq "C") {CertificateActionSelection}
} Until ($MoreCertificateActions -eq "Q")
Remove-Item $FFCertLocationLocal -Recurse
Set-ExecutionPolicy $ExecutionPolicyOriginal
Exit
I had a similar issue on a client site where the client required a authority certificate to be installed automatically for 2000+ windows users.
I created the following .vbs script to import the certificate into the current logged on users firefox cert store.
The script needs to be put in the directory containing a working copy of certutil.exe (the nss version) but programatically determines the firefox profiles location.
Option Explicit
On error resume next
Const DEBUGGING = true
const SCRIPT_VERSION = 0.1
Const EVENTLOG_WARNING = 2
Const CERTUTIL_EXCUTABLE = "certutil.exe"
Const ForReading = 1
Dim strCertDirPath, strCertutil, files, slashPosition, dotPosition, strCmd, message
Dim file, filename, filePath, fileExtension
Dim WshShell : Set WshShell = WScript.CreateObject("WScript.Shell")
Dim objFilesystem : Set objFilesystem = CreateObject("Scripting.FileSystemObject")
Dim certificates : Set certificates = CreateObject("Scripting.Dictionary")
Dim objCertDir
Dim UserFirefoxDBDir
Dim UserFirefoxDir
Dim vAPPDATA
Dim objINIFile
Dim strNextLine,Tmppath,intLineFinder, NickName
vAPPDATA = WshShell.ExpandEnvironmentStrings("%APPDATA%")
strCertDirPath = WshShell.CurrentDirectory
strCertutil = strCertDirPath & "\" & CERTUTIL_EXCUTABLE
UserFirefoxDir = vAPPDATA & "\Mozilla\Firefox"
NickName = "Websense Proxy Cert"
Set objINIFile = objFilesystem.OpenTextFile( UserFireFoxDir & "\profiles.ini", ForReading)
Do Until objINIFile.AtEndOfStream
strNextLine = objINIFile.Readline
intLineFinder = InStr(strNextLine, "Path=")
If intLineFinder <> 0 Then
Tmppath = Split(strNextLine,"=")
UserFirefoxDBDir = UserFirefoxDir & "\" & replace(Tmppath(1),"/","\")
End If
Loop
objINIFile.Close
'output UserFirefoxDBDir
If objFilesystem.FolderExists(strCertDirPath) And objFilesystem.FileExists(strCertutil) Then
Set objCertDir = objFilesystem.GetFolder(strCertDirPath)
Set files = objCertDir.Files
For each file in files
slashPosition = InStrRev(file, "\")
dotPosition = InStrRev(file, ".")
fileExtension = Mid(file, dotPosition + 1)
filename = Mid(file, slashPosition + 1, dotPosition - slashPosition - 1)
If LCase(fileExtension) = "cer" Then
strCmd = chr(34) & strCertutil & chr(34) &" -A -a -n " & chr(34) & NickName & chr(34) & " -i " & chr(34) & file & chr(34) & " -t " & chr(34) & "TCu,TCu,TCu" & chr(34) & " -d " & chr(34) & UserFirefoxDBDir & chr(34)
'output(strCmd)
WshShell.Exec(strCmd)
End If
Next
WshShell.LogEvent EVENTLOG_WARNING, "Script: " & WScript.ScriptFullName & " - version:" & SCRIPT_VERSION & vbCrLf & vbCrLf & message
End If
function output(message)
If DEBUGGING Then
Wscript.echo message
End if
End function
Set WshShell = Nothing
Set objFilesystem = Nothing
I have an update of this awesome answer (just not working any more with last Firefox updates), in this same thread, made by H.-Dirk Schmitt, also thanks to the answer in this other thread made by BecarioEstrella.
I just adapted the script to recent changes.
Tested in 2021 just in Firefox 85.0.1 (64bit) in Ubuntu 20.04 and 18.04.
#!/usr/bin/env bash
function usage {
echo "Error: no certificate filename or name supplied."
echo "Usage: $ ./installcerts.sh <certname>.pem <Cert-DB-Name>"
exit 1
}
if [ -z "$1" ] || [ -z "$2" ]
then
usage
fi
certificate_file="$1"
certificate_name="$2"
for certDB in $(find ~/.mozilla* ~/.thunderbird -name "cert9.db")
do
cert_dir=$(dirname ${certDB});
echo "Mozilla Firefox certificate" "install '${certificate_name}' in ${cert_dir}"
certutil -A -n "${certificate_name}" -t "TCu,Cuw,Tuw" -i ${certificate_file} -d sql:"${cert_dir}"
done
If you want it just for Firefox, replace the line:
for certDB in $(find ~/.mozilla* ~/.thunderbird -name "cert9.db")
By
for certDB in $(find ~/.mozilla* -name "cert9.db")
Further readings:
NSS Shared DB
NSS Shared DB Howto