Posted by Shane Corellian on Mon, Aug 16, 2010
A question came into us this week about customizing Adobe Flash Player installations. There certainly is a way to deploy customized Flash Players however it's not incredibily intuitive. Unlike other Windows Installer (.msi) applications Flash isn't modified via Transforms files or Windows Installer options.
Before you can deploy Adobe products, you need to enter a license agreement with Adobe. Shawn wrote an article outlining how this can be done. After entering the agreement with Adobe you will receive a link to download the deployable products requested.
http://www.adminarsenal.com/admin-arsenal-blog/bid/38437/Customizing-Deploying-Adobe-Reader-9-3
Adobe Flash Player looks in the %WINDIR%\System32\Macromed\Flash directory for the file mms.cfg. This file contains (or can contain) configuration settings which vary from disabling Auto Updates to disabling Flash access to the file system or network.
Adobe provides a great reference document detailing Flash Player Administration. The options for your mms.cfg file are located on Page 19.
I decided to disable both the Automatic Update and Flash access to webcams and microphones. Below are the values in my mms.cfg file:
#Disable AutoUpdate
AutoUpdateDisable=true
#Prevent SWF files from accessing Webcams or Microphones
AVHardwareDisable=true
As I need to perform a custom action AFTER the install I decided to wrap the installation up in a CMD file. Below are the contents of my InstallFlash.cmd file.
\\scranton\deploy\Adobe\FlashPlayer\install_flash_player_10_active_x.msi /quiet /norestart IF %ERRORLEVEL% == 0 goto MMS
exit %ERRORLEVEL%
:MMS
IF EXIST %WINDIR%\System32\Macromed\Flash (
copy /Y \\scranton\deploy\Adobe\FlashPlayer\mms.cfg %WINDIR%\System32\Macromed\Flash
) ELSE (
mkdir %windir%\System32\Macromed\Flash
copy /Y \\scranton\deploy\Adobe\FlashPlayer\mms.cfg %WINDIR%\System32\Macromed\Flash
)
exit %ERRORLEVEL%
Here is a screenshot of the deployment summary in PDQ Deploy.

The deployment runs remarkably fast (less than 20 seconds in my different deployments).

Use PDQ Deploy. It's free. It's fully functional. It kicks copious amounts of ass.
Follow me on twitter @ShaneCorellian
Posted by Adam Ruth on Mon, Jul 12, 2010
One feature of PowerShell that I had a hard time figuring out was how to include one script in another. I have a number of build scripts which build the installers for our products, and there are a lot of steps that are the same between them. Up until recently I was using the proven method of copy/paste to share the functionality, but I finally decided to sit down and take the time to build out my build system properly.
The first thing I needed to do was to put similar functionality into a library and load it into each product's build script. This library consists of a bunch of functions such as incrementing version numbers, building help files, compiling source code, and digitally signing files. The problem is how to get the functions to be available to other scripts. As you may have noticed, when you run a script none of the functions are available to you after the script ends. Here's me running a script that has a single function called Hello:

In order to make the function in the script last after the script ends, it's necessary to run the script preceded by a period:

With that, all symbols in the script persist and you can create any library scripts you may need.
Once you get to the point where you have enough scripts to make modularity necessary, you are to the point you should consider using some source control (for a primer read my whitepaper on the topic.)
Have any other PowerShell tips that may not be obvious to everyone? Post them in the comments and share with the world (or the tiny portion of it that reads this blog, anyway.)
Posted by Shane Corellian on Fri, Jun 11, 2010
In the course of our duties as Sys Admins I'm sure we've all "pushed the wrong button" a few times. If you haven't yet, you probably haven't been a Sys Admin very long.
About 10 years ago while working on a project which was wrought with political and technical paranoia I decided to prepare my own "Lysine Contingency" in the event that the political hailstorm became too much for me to handle. I first heard the term Lysine Contingency (LC) from Jurassic Park. I was always intrigued by the idea that Samuel L.'s character introduced. Certain destruction to the park's dinosaurs if a problem occurred that was simply unmanageable.
A Lysine Contingency is not your normal Change Management Rollback plan. It is basically you screaming "MISSION COMPROMISED! ABORT ABORT".
So, I want to offer some things I have learned about Lysine Contingency plans.
Points to consider:
- How was the change implemented?
- Were there multiple execution points such as Group Policy, Software Deployment, Login Scripts, Manual Installations, cron jobs or other local schedulers?
- Which execution point can be used to produce the greatest yield on your rollback? If you can hit 80% of your computers in 20 minutes with Software Deployment vs. 1 day with Group Policies then it's obvious where you should place your focus.
- Can the change be rolled back or otherwise mitigated without affecting other production systems, users or applications?
- Does your change require a reboot of the target system or application?
- What, if any, are the dependent services or applications?
- Will any data be lost? Are there other methods of removing the change that may take longer but save the data?
- Do you need the cooperation of any external entity?
- Are you allowed to deploy software but you're not allowed to modify GPOs? Are the GPOs part of your LC?
- The best Lysine solutions are the solutions that don't require anyone else but you. In a serious do-or-die situation you don't want to rely on the guy who's always on a smoking break or the lady who has more drama in her life than the evening lineup on Lifetime.
I want to stress: I'm not talking about proper Change Management procedures here. You should always have a rollback plan for major changes in your computing environment. I have had to rollback many changes to my various environments over the years, however, I have only had to enact a Lysine Contingency once and this came over two years AFTER the changes were introduced! Remember, the issues that often require your Lysine Contingency are just as easily going to be political as they are technical.
In the case I mentioned earlier I had my entire LC whittled down to one script. When the alarm sounded I knew that I had prepared for this scenario and there was no need to script some uninstallation routine or quickly write a new GPO or even to hold a meeting. This script set one flag which my verification utility read as "get the hell out of here!". With this flag, no more new verifications or installations would take place via GPO. The next section of my script deployed a Tivoli Software Package which immediately uninstalled the utility on all Windows computers. The next section added some log files to a central server. These files detailed the actions taken and would, ultimately, record the success rate of the Lysine Contingency.
Within 30 minutes the only traces of the original changes were contained in logs which I promptly gave to my manager. The Lysine Contingency went off without a hitch so that when the musical chairs music stopped I wasn't the one left standing.
Follow me on Twitter @ShaneCorellian
Posted by Adam Ruth on Fri, Apr 16, 2010

Photo by *yasuhiro
I find myself having to deal with Windows services quite a lot, probably more than the average system administrator. The two most common tools administrators use are the services.msc MMC snap-in and net.exe (net start and net stop, in particular.) One more tool that I keep close is sc.exe because it gives capabilities that you can't find in the other tools.
It provides pretty much everything that a developer can do when programming directly to the Service Control Manager. The commands that I use most often are create and delete. These are particularly useful when I'm writing a service and I need to test it on one or more machines.
Creating a Service
The create command has the following syntax:
sc.exe <server> create [service name] [binPath= ] <option1> <option2>…
Run "sc.exe create" to see all of the options. The ones you'll use most are:
- start= (auto, manual, disabled)
- obj= (account name)
- password= (password)
- DisplayName= (friendly name)
There are some gotchas that you may run into (I know I have!):
- If using PowerShell you need to use sc.exe instead of just sc since sc is an alias for the built-in cmdlet Set-Content.
- If you get the syntax wrong you won't get an explanation of what why, you'll only get the usage description so it can be difficult to track down typos.
- All of the options follow the same syntax of "binPath= path." Note that there is no space before the equal sign and a space afterwards. That's caught me many times, the command will choke on "binPath = path" and "binPath=path."
- You'll most likely need quotes in the binPath= parameter. For example if the service path is "C:\Program Files\Company\Name\Service.exe" -service you'll need to escape the quotes. This is done differently if you're using PowerShell or cmd.exe:
- PowerShell: sc.exe create name binPath= '\"C:\Program Files\Company\Name\Service.exe\" -service'
Note the \ before the double-quotes and the whole thing is wrapped in single-quotes. - cmd.exe: sc.exe create name binPath= """"C:\Program Files\Company\Name\Service.exe""" -service"
Note that it's wrapped in double-quotes and the inner quotes are three sets of double-quotes.
Editing a Service
There is a config command that lets you change all of the service's settings without re-creating it. It has the same options as the create command.
Deleting a Service
Deleting a service is a lot simpler:
sc.exe <computer> delete [service name]
If the service is still running when you do this, it will be "marked for deletion" which is a kind of limbo state where the service can't be controlled any more (can't be stopped.) If that happens, most of the time you can flush the delete by killing the service's process. In the rare case where that doesn't work, a reboot will be required.
Services on Other Computers
In order to work with services remotely on other computers you need to have File Sharing turned on and opened through a firewall. If you can get to a file share on the computer, you'll be able to modify its services.
Posted by The Admin Arsenal Team on Mon, Jun 01, 2009

Photo by timsamoff
Don't you get a kick out of looking at old scripts that you've built? I've been going through some fossils. I'm pretty sure that some Carbon-14 (14C) testing is needed to date some of these scripts. While some scripts will never be used again, some are still relevant and useful.
I remember that Adam and I were asked to report (via Tivoli Inventory) which computers had CAC (or SmartCard) Readers. This information wasn't available "out of the box" in Tivoli so we wrote a custom Inventory scanner. I've kept the MIF creation logic (see, doesn't MIF take you back?) for giggles. I still use Devcon.exe(which is referenced in the script).
#!/usr/bin/perl
############################################################################
#
# This script will scan and collect information about SmartCard Readers
# This script depends on devcon.exe (which is freely available)
#
# SPC - 25 March 2005 - Original Script written
############################################################################
use Win32;
$dev='device';
$status='status';
sub main {
chkver();
wrtmif();
rundevcon();
closemif();
}
sub chkver {
#Devcon cannot run on NT 4.0 systems. Exclude all NT 4.0 from executing.
($string, $major, $minor, $build, $id) = Win32::GetOSVersion();
print "Major = $major\n";
if ($major == 4) {
print "This script will not run on Windows NT Version $major\n";
exit
}
}
sub wrtmif {
#write scardrdr.mif file
$cMif="scardrdr.mif";
open(FILE,">$cMif") || die "Could not open MIF file";
print (FILE "START COMPONENT\n");
print (FILE "\tNAME = \"SCARDRDR.MIF\"\n");
print (FILE "\tDESCRIPTION = \"Smart Card Readers installed on System\"\n");
print (FILE "\tSTART GROUP\n");
print (FILE "\t\tNAME = \"ESM_SMART_CARD_READERS\"\n");
print (FILE "\t\tID = 1\n");
print (FILE "\t\tCLASS = \"ESM_SMART_CARD_READERS|1\"\n");
print (FILE "\t\tSTART ATTRIBUTE\n");
print (FILE "\t\t\tNAME = \"HWID\"\n");
print (FILE "\t\t\tID = 1\n");
print (FILE "\t\t\tACCESS = READ-ONLY\n");
print (FILE "\t\t\tTYPE = STRING(255)\n");
print (FILE "\t\t\tVALUE = \"\"\n");
print (FILE "\t\tEND ATTRIBUTE\n");
print (FILE "\t\tSTART ATTRIBUTE\n");
print (FILE "\t\t\tNAME = \"Device\"\n");
print (FILE "\t\t\tID = 2\n");
print (FILE "\t\t\tACCESS = READ-ONLY\n");
print (FILE "\t\t\tTYPE = STRING(255)\n");
print (FILE "\t\t\tVALUE = \"\"\n");
print (FILE "\t\tEND ATTRIBUTE\n");
print (FILE "\t\tSTART ATTRIBUTE\n");
print (FILE "\t\t\tNAME = \"STATUS\"\n");
print (FILE "\t\t\tID = 3\n");
print (FILE "\t\t\tACCESS = READ-ONLY\n");
print (FILE "\t\t\tTYPE = STRING(512)\n");
print (FILE "\t\t\tVALUE = \"\"\n");
print (FILE "\t\tEND ATTRIBUTE\n");
print (FILE "\t\tKEY = 1\n");
print (FILE "\tEND GROUP\n");
print (FILE "\tSTART TABLE\n");
print (FILE "\t\tNAME = \"SCARDRDR.MIF\"\n");
print (FILE "\t\tID = 1\n");
print (FILE "\t\tCLASS = \"ESM_SMART_CARD_READERS|1\"\n");
}
sub rundevcon {
#run Devcon and grab all info regarding CAC Readers
@dev = `devcon.exe status =SmartCardReader`;
$grabDev = 1;
$readName = 0;
$noDev = 1;
foreach $line (@dev) {
$line =~ s/^\s+//;
$line =~ s/\s+$//;
if ($grabDev) {
chomp $line;
$hwid=$line;
print "HW ID = $hwid\n";
$grabDev='';
}
if ($readName) {
print 'readname is running';
chomp $line;
$status=$line;
add2mif();
$readName='';
$noDev=0;
$grabDev = 1;
}
if ($line =~ /^Name: (.+)/) {
$dev=$1;
print "Device Name is: $dev\n";
$readName = 1;
}
}
if ($noDev) {
$hwid='0000';
$dev='No Devices Found';
$status='DISABLED';
add2mif();
}
}
sub add2mif {
#add data content to scardrdr.mif file
print (FILE "\t\t\t{\"$hwid\",\"$dev\",\"$status\"}\n");
}
sub closemif {
#finish writing mif file
print (FILE "\tEND TABLE\n");
print (FILE "END COMPONENT\n");
close (FILE);
}
main();
I guess Mom was wrong when she said "if you haven't used it in six months, throw it away". I always did hate cleaning out the attic.
Posted by The Admin Arsenal Team on Wed, May 20, 2009
I have been a huge fan of WinBatch since 1997. WinBatch, a fantastic scripting utility provided by Wilson WindowWare, is based on the Windows Interface Language (WIL).
I have gotten out of more than one tight spot with WinBatch. I intend to use this forum to post scripts which I have used through the years which have helped me with my systems management duties.
The following script is designed to open up the Registry via Regedit.exe. It will then connect to a remote machine (based off of the Argument passed). In other words, if I wanted to open the registry of a remote computer called Homer, I would simply just have to run
StartReg.exe homer
Below is the WinBatch script
; This script will open a separate window for Regedit and connect to the target machine
ErrorMode(@Off)
systemroot=Environment("SystemRoot")
if isDefined(param1)
param1 = strTrim(param1)
if FileExist(StrCat(systemroot,"regedit.exe"))
run(StrCat(systemroot,"regedit.exe"), "-m")
winCheck = WinWaitExist("Registry Editor", 5)
if winCheck == @False then exit
IgnoreInput(@TRUE)
GetWindow = DllHwnd("Registry Editor")
ID=WinIdGet("Registry Editor")
winActivate(ID)
SendKeysTo(ID, "!FC")
scCheck = WinWaitExist("Select Computer", 4)
if scCheck == @False
IgnoreInput(@FALSE)
display(5, "Error", "Error connecting to Remote Registry")
exit
endif
SCID = WinIdGet("Select Computer")
SendKeysTo(SCID, "!E")
SendKeysTo(SCID, param1)
SendKeysTo(SCID, "!c")
MouseClickBtn(SCID, "", "OK")
IgnoreInput(@FALSE)
endif
else
message("Error", "Please pass name of remote computer as argument (e.g. StartReg.exe homer)")
endif
There you go. Unfortunately I have NOT found a better way to automatically connect to a remote registry without having to automate the key strokes. Oh well, it may be kludgy but it works.