• Google Chrome for the Mac – Pin tab is back

    5.0.366.0 dev seems to have added pin tab back in. Let’s hope it stays. Thanks Mr. Google.


  • Powershell signing, makecert.exe and group policy – Part 2

    In the previous post I showed how to create self signed signing certs via the makecert tool. Once I made the pfx files, and signed my script, I need to make the server I was working see the certificate chain. I created a GPO and applied it to the server OU. I added the Root cert to Trusted Root Certification Authorities, and I added the Cert to Trusted Publishers Certificates. Now when I open a signed script on a server, I no longer have to change the Set-ExecutionPolicy RemoteSigned and add the network path to the Local Intranet zone.


  • Powershell signing, makecert.exe and group policy – Part 1

    We don’t have a pki infrastructure at my current job, and I like to keep my powershell scripts on my network drive. So I needed a way to run my .ps1 files off a network drive. Setting Set-ExecutionPolciy to Unrestricted seemed like a bad idea. So I looked into using a signed script and setting Set-ExecutionPolicy to RemoteSigned. I could not justify $300 to buy a trused third party cert, so I looked at makecert.exe from the Windows SDK.

    First step make the Root CA cert, private key, and pxf.

    1. makecert -n “CN=RootName” -a sha1 -eku 1.3.6.1.5.5.7.3.3 -r -sv Root.pvk Root.cer
    2. pvk2pfx.exe -pvk Root.pvk -spc Root.cer -pfx Root.pfx -pi password

    makecert and pvk2pfx can be found in the Windows SDK or in the Visual Studio bin directory. First command results in a private key (.pvk) and a certificate (.cer). Second command makes a pfx out of the first 2.

    Second step is to create a certificate from the root cert above.

    1. makecert -pe -n “CN=Certificate” -a sha1 -eku 1.3.6.1.5.5.7.3.3 -ic Root.cer -iv Root.pvk -sv Certificate.pvk Certificate.cer
    2. pvk2pfx.exe -pvk Certificate.pvk -spc Certificate.cer -pfx Certificate.pfx -pi password

    Now we are ready to sign our powershell script.

    1. $cert = Get-PfxCertificate Certificate.pfx
    2. Set-AuthenticodeSignature -Filepath script.ps1 -Cert $cert

    Probably can be combined into one line, but I am not a guru yet.

    Next post will talk about how I added this certs above to all the servers.


  • New MD5 based backup script

    I found this use of md5 and find the other day. I based my current backup script around it. The md5 will show if anyone modifies a file, or adds/removes a file in the web hosting root (/var/www) or in the config directory (/etc/httpd/conf.d/). If there is a change then zip each site up individually and move to a backup folder to be rsynced to other servers.

    NewWWWMD5=$(find /var/www/ -type f -exec md5sum {} \; | md5sum - | awk '{print $1}')
    OldWWWMD5=$(cat $PARENTDIR/_var_www_*.md5)
    NewConfMD5=$(find /etc/httpd/conf.d/ -type f -exec md5sum {} \; | md5sum - | awk '{print $1}')
    OldConfMD5=$(cat $PARENTDIR/_etc_httpd_conf.d_*.md5)
    
    if [ $NewWWWMD5 = $OldWWWMD5 -a $NewConfMD5 = $OldConfMD5 ]; then
    	echo "Neither /var/www/ nor /etc/httpd/conf.d/ have changed"
    else
    	rm -rf $BACKUPDIR/*Files
    	echo "/var/www or /etc/httpd/conf.d has changed"
    	mkdir -p $BACKUPDIR-Files
    
    	# backup /var/www
    	for directory in /var/www/*; do
    	  	if [ -d $directory ]; then
    	    	bu $directory;
    	    fi
    	done
    
    	# replace previous /var/www MD5
    	rm -f $PARENTDIR/_var_www_*.md5
    	find /var/www/ -type f -exec md5sum {} \; | md5sum - | awk '{print $1}' > $PARENTDIR/_var_www_$CURRENTDAY.md5
    
    	#backup /etc/httpd/conf.d
    	bu "/etc/httpd/conf.d"
    
    	# replace previous /etc/httpd/conf.d MD5
    	rm -f $PARENTDIR/_etc_httpd_conf.d_*.md5
    	find /etc/httpd/conf.d/ -type f -exec md5sum {} \; | md5sum - | awk '{print $1}' > $PARENTDIR/_etc_httpd_conf.d_$CURRENTDAY.md5
    fi

    Seems to work!


  • PowerShell is great

    Okay, I drank the Kool-Aid. PowerShell is awesome. The first thing I wanted to do was make sure my $profile is always the most current no matter what machine was on. In the past I had a batch file that I would run that would open my command prompt the way I wanted. I modified that so if PowerShell is installed, then get my profile up-to-date and then launch PowerShell. This is part of my current batch file:

    IF NOT EXIST "c:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" GOTO NO_PS
    powershell -NoProfile $a=(Split-Path $profile -parent);if (!(Test-Path $a)) {New-Item $a -type directory};
    powershell -NoProfile if (!(Test-Path $profile)) {Copy-Item %~dp0Scripts\Microsoft.PowerShell_profile.ps1 (Split-Path $profile -parent)}
    powershell -NoProfile if (!(Compare-Object $(Get-Content $profile) $(Get-Content Scripts\Microsoft.PowerShell_profile.ps1)).Count -eq 0 ) {Copy-Item Scripts\Microsoft.PowerShell_profile.ps1 (Split-Path $profile -parent)}
    @start  %COMSPEC% /K PowerShell -nologo

    This will copy over my profile if it does not exist or is a different version. Now when I am on a server with PowerShell I can double click my batch file and my environment is up to date!


  • Using a sub-select to find machines that do not have the most recent version of a package.

    Many people have blogged about this – how to find machines that don’t have the most recent version of a package installed.

    First we write a query to show machines that don’t have the software installed (in this case firefox)
     select SMS_R_System.Name,SMS_R_System.LastLogonUserName
    	from SMS_R_System
    		inner join SMS_G_System_SYSTEM on SMS_G_System_SYSTEM.ResourceID = SMS_R_System.ResourceId
    	where SMS_R_System.Client = 1
    		and SMS_G_System_SYSTEM.SystemRole = "Workstation"
    		and SMS_G_System_SYSTEM.Name not in (
    			select SMS_R_System.Name
    			from  SMS_R_System inner join SMS_G_System_ADD_REMOVE_PROGRAMS on SMS_G_System_ADD_REMOVE_PROGRAMS.ResourceID = SMS_R_System.ResourceId
    			where SMS_R_System.Client = 1
    			and SMS_G_System_ADD_REMOVE_PROGRAMS.DisplayName like "Mozilla Firefox%")
    
    Next we write a query to show the machines that have the most recent software installed (this is used in the following query):
    select SMS_R_System.Name, SMS_R_System.LastLogonUserName, SMS_G_System_ADD_REMOVE_PROGRAMS.DisplayName,
    			SMS_G_System_ADD_REMOVE_PROGRAMS.Version
    	from  SMS_R_System
    		inner join SMS_G_System_ADD_REMOVE_PROGRAMS on SMS_G_System_ADD_REMOVE_PROGRAMS.ResourceID = SMS_R_System.ResourceId
    	where SMS_R_System.Client = 1
    		and SMS_G_System_ADD_REMOVE_PROGRAMS.DisplayName like "Mozilla Firefox%"
    		and SMS_G_System_ADD_REMOVE_PROGRAMS.Version = "3.6.3 (en-US)"
    	order by SMS_R_System.Name

    Finally we write a query to show machines that aren’t in the query above

    	select SMS_R_System.Name, SMS_R_System.LastLogonUserName, SMS_G_System_ADD_REMOVE_PROGRAMS.DisplayName, SMS_G_System_ADD_REMOVE_PROGRAMS.Version
    	from  SMS_R_System inner join SMS_G_System_ADD_REMOVE_PROGRAMS on SMS_G_System_ADD_REMOVE_PROGRAMS.ResourceID = SMS_R_System.ResourceId
    	where SMS_R_System.Client = 1
    		and SMS_G_System_ADD_REMOVE_PROGRAMS.DisplayName like "Mozilla Firefox%"
    		and SMS_R_System.Name not in (
    			select SMS_R_System.Name
    			from  SMS_R_System inner join SMS_G_System_ADD_REMOVE_PROGRAMS on SMS_G_System_ADD_REMOVE_PROGRAMS.ResourceID = SMS_R_System.ResourceId
    			where SMS_R_System.Client = 1
    			and SMS_G_System_ADD_REMOVE_PROGRAMS.DisplayName like "Mozilla Firefox%"
    			and SMS_G_System_ADD_REMOVE_PROGRAMS.Version = "3.6.3 (en-US)")
    order by SMS_R_System.Name

    Import the First and Third queries into a collection and we have a collection that shows machines that need the updated package (including machines that don’t have any version of the package installed.)


  • How I created a “Copy to new item” functionality for a SharePoint list – Part 2

    Second part. First Part can be found here. On the second page (NewForm.aspx), I grabbed out of the querystring the values for the source list item and the name of the list.
    I used this person’s query string parser.

    Then I used the following SOAP query :

    $(document).ready(function() {
     var sourceID = getQuerystring('SourceID');
     var listName = getQuerystring('ListName');
    
     var soapEnv = "<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/'> \
    <soapenv:Body> \
    <GetListItems xmlns='http://schemas.microsoft.com/sharepoint/soap/'> \
    <listName>" + listName + "</listName> \
    <viewName>{GUID}</viewName> \
    <viewFields /> \
    <ViewFields /> \
    <query> \
    <Query><Where> \
    <Eq> \
    <FieldRef Name='ID' /> \
    <Value Type='Integer'>" + sourceID + "</Value> \
    </Eq> \
    </Where></Query>\
    </query> \
    </GetListItems> \
    </soapenv:Body> \
    </soapenv:Envelope>";
    
     $.ajax({
     async: false,
     url: "http://site.com/subsite/_vti_bin/lists.asmx",
     type: "POST",
     dataType: "xml",
     data: soapEnv,
     complete: processResult,
     contentType: "text/xml; charset=\"utf-8\""
     });
    
     });
     function processResult(xData, status) {
     $(xData.responseXML).find("z\\:row").each(function() {
     $("input[title='Input']").val($(this).attr("ows_Input"));
     $("textarea[title='TextArea']").val($(this).attr("ows_TextArea"));
     $("select[title='DropDown']").val($(this).attr("ows_DropDown")).attr("selected", "selected"); 
     }
     )};
    

    In the last three lines I changed the input box, textbox and dropdown boxes to their values in the results fromt the SOAP query.

    Kinda fun. Just need to figure out how to do check boxes!


  • How I created a “Copy to new item” functionality for a SharePoint list – Part 1

    I wanted to create a “copy to new item” functionality for a SharePoint list.

    Steps I came up with:

    1. The first thing I had to do was add a link to the context menu (I learned that it is called an ECB) and have it point to the NewForm.aspx.
    2. Once I got that, I could append a querystring variable to the url that contained the “Source ID” of the item to copy and the name of the current list.
    3. Then I would grab that querystring value on the other side – in the NewForm.aspx page
    4. Next I would use that variable to query the SharePoint List via SOAP
    5. Inject the results the  into the form

    Here is my script (add to a CEWP, I already have the jquery pointers to google in a Delegate control)

    <script language="javascript">
    function Custom_AddListMenuItems(m, ctx) {
    var editURL = window.location.protocol + "//" + window.location.host + ctx.listUrlDir + "/NewForm.aspx?SourceID=" + currentItemID + "&amp;ListName=" + ctx.ListTitle;
     CAMOpt(m, "Copy To New Item" ,"window.location=('" + editURL + "');" , "/_layouts/images/Copy.GIF");
     CAMSep(m);
    }
    </script>
    

    More to come.