• PowerShell script to create a SCCM Package,Program and Queries

    I wanted to automate the process of creating a new package. The following code will look at an XML file for values (same xml file I am using here) and create a package based on the values. A lot of SCCM PowerShell was gathered from here. Here is the example of the source XML file:

    <Install>
    <Params>
    <PkgName Value="Firefox" />
    <PkgManufacturer Value="Mozilla" />
    <PkgVersion Value="12.0" />
    <PkgSourcePath Value="\\domain.local\DFSRoot\Path\Mozilla\Firefox-12.0" />
    <PkgcontainerNodeID Value="17" />
    <CurrentVersionQueryName Value="Software-MozillaFirefox-12.0" />
    <OtherThanQueryName Value="Software-MozillaFirefox-OtherThan-12.0" />
    <QuerycontainerNodeID Value="4" />
    </Params>
    </Install>
    

    And here is the script that creates the package, program, distribution point,and 2 queries (A query to show where version is installed, and a second to show where it is not installed (NOT IN).)

    function JBMMURPHY-SCCM-CreatePackageAndQueries{
    PARAM(
        [Parameter(Mandatory=$true)]
        [ValidateScript({Test-Path $_})]
        $XMLFilePath)
    [ xml ]$s = Get-Content $XMLFilePath
    
    $NAMESPACE="root\SMS\Site_LAB"
    $PkgName=$s.Install.SCCMParams.PkgName.Value
    $PkgManufacturer=$s.Install.SCCMParams.PkgManufacturer.Value
    $PkgVersion=$s.Install.SCCMParams.PkgVersion.Value
    $PkgSourcePath=$s.Install.SCCMParams.PkgSourcePath.Value
    $PkgcontainerNodeID=$s.Install.SCCMParams.PkgcontainerNodeID.Value
    $OtherThanQueryName=$s.Install.SCCMParams.OtherThanQueryName.Value
    $CurrentVersionQueryName=$s.Install.SCCMParams.CurrentVersionQueryName.Value
    $QuerycontainerNodeID=$s.Install.SCCMParams.QuerycontainerNodeID.Value
    $PkgProgramFlags=135308289
    $PkgCommandLine = "powershell .\SVCInstall.ps1"
    
    ## create package and move into appropriate folder
    $PackageProperties = @{
    Name = $PkgName;
    Manufacturer = $PkgManufacturer;
    Version = $PkgVersion;
    Language = "English (United States)";
    PackageType = 0;
    PkgSourceFlag = 2;
    PkgSourcePath = $PkgSourcePath
    Priority = 2
    }
    $newPackage = Set-WmiInstance -class SMS_Package -arguments $PackageProperties -namespace $NAMESPACE
    $newPackage | Format-List | Out-Null
    write-host "pkgid=$($newPackage.PackageID)"
    
    ## Move Package
    $MovePackage = @{
    InstanceKey = $newPackage.PackageID;
    ObjectType = 2;
    ContainerNodeID = $PkgcontainerNodeID
    }
    Set-WmiInstance -Class SMS_ObjectContainerItem -arguments $MovePackage -namespace $NAMESPACE
    
    ## create new program = Per-system unattended
    
    $NewProgram = @{
    PackageID = $newPackage.PackageID;
    ProgramFlags = $PkgProgramFlags;
    ProgramName = "Per-system unattended";
    CommandLine = $PkgCommandLine
    }
    Set-WmiInstance -class SMS_Program -arguments $NewProgram -namespace $NAMESPACE
    
    ## Add Distribution point to package
    
    $NewDistPointForPackage = @{
    PackageID = $newPackage.PackageID;
    ServerNALPath = '["Display=\\SERVENAME\"]MSWNET:["SMS_SITE=LAB"]\\SERVERNAME\';
    SiteCode = "LAB";
    SiteName = "LAB";
    SourceSite = "LAB";
    ResourceType = "Windows NT Server"
    }
    Set-WmiInstance -class SMS_DistributionPoint -arguments $NewDistPointForPackage -namespace $NAMESPACE
    
    
    ## Creating Queries
    
    ## Current Verson Query
    $CurrentVersionQuery="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 `'$($PkgName)%`' and SMS_G_System_ADD_REMOVE_PROGRAMS.Version = `'$($PkgVersion)`' order by SMS_R_System.Name";
    $CurrentVersionQueryHash = @{
    Name = $CurrentVersionQueryName;
    Expression = $CurrentVersionQuery;
    TargetClassName = "SMS_R_System"
    }
    $newCurrentVersionQuery = Set-WmiInstance -class SMS_Query -arguments $CurrentVersionQueryHash -namespace $NAMESPACE
    $newCurrentVersionQuery  | Format-List | Out-Null
    
    $MoveCurrentVersionQuery=@{
    InstanceKey = $newCurrentVersionQuery.QueryID;
    ObjectType = 7;
    ContainerNodeID = $QuerycontainerNodeID
    }
    Set-WmiInstance -Class SMS_ObjectContainerItem -arguments $MoveCurrentVersionQuery -namespace $NAMESPACE
    
    
    ## Other Than Query
    $OtherThanQuery="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 `'$($PkgName)%`' 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 `'$($PkgName)%`' and SMS_G_System_ADD_REMOVE_PROGRAMS.Version = `'$($PkgVersion)`') order by SMS_R_System.Name"
    $OtherThanQueryHash =@{
    Name = $OtherThanQueryName;
    Expression = $OtherThanQuery;
    TargetClassName = "SMS_R_System"
    }
    $newOtherThanQuery = Set-WmiInstance -class SMS_Query -arguments $OtherThanQueryHash -namespace $NAMESPACE
    $newOtherThanQuery  | Format-List | Out-Null
    
    $MoveOtherThanQuery=@{
    InstanceKey = $newOtherThanQuery.QueryID;
    ObjectType = 7;
    ContainerNodeID = $QuerycontainerNodeID
    }
    Set-WmiInstance -Class SMS_ObjectContainerItem -arguments $MoveOtherThanQuery -namespace $NAMESPACE
    
    # To Find Node: gwmi -Namespace $NAMESPACE -query "Select * from SMS_ObjectContainerNode WHERE Name = 'ContainerName'"
    # Example run: JBMURPHY-SCCM-CreatePackage -XMLFilePath \\domain.local\DFSRoot\Path\Install.xml
    #
    }
    

  • PowerShell function to search CRM 2011 for and Entity (via REST/oData)

    I wrote about how to retrieve records from CRM 2011 via oData. I wanted to wrap that up in a function that I can use to do a quick search:

    
    Function JBMURPHY-CRM-SearchEntity{
    PARAM([parameter(Mandatory=$true)][ValidateSet("Contact", "Account","SystemUser")]$EntityType,[parameter(Mandatory=$true)]$SearchString,[parameter(Mandatory=$true)]$SearchField,$FieldsToReturn)
    $assembly = [Reflection.Assembly]::LoadWithPartialName("System.Web.Extensions")
    $baseurl="http://crmserver.company.com/Organization/xrmservices/2011/OrganizationData.svc"
    $urlparams="/$($EntityType)Set?`$filter=substringof('$($SearchString)',$($SearchField))"
    $url=$baseurl+$urlparams
    $Count=0
    while ($url){
        $webclient = new-object System.Net.WebClient
        $webclient.UseDefaultCredentials = $true
        $webclient.Headers.Add("Accept", "application/json")
        $webclient.Headers.Add("Content-Type", "application/json; charset=utf-8");
        $dataString=$webclient.DownloadString($url)
        $json=new-object System.Web.Script.Serialization.JavaScriptSerializer
        $data=$json.DeserializeObject($dataString)
        foreach ($result in $data.d.results){
                $Count=$Count+1
                write-host -NoNewline "$Count. "
                foreach ($field in $FieldsToReturn){
                write-host -NoNewline "$field : "
                Write-Host -NoNewline $result."$field"
                Write-Host -NoNewline ", "
                #write-host "$($result.FullName) , $($result.EMailAddress1)"
                }
                Write-Host
        }
        if ($data.d.__next){
            $url=$data.d.__next.ToString()
        }
        else {
            $url=$null
        }
    }
    }
    
    

    And to use this function:

    JBMURPHY-CRM-SearchEntity -EntityType Account -SearchField Name -SearchString “Sard” -FieldsToReturn Name,AccountId


  • Using PowerShell to query CRM 2011 SOAP endpoint – Answer!

    Big thanks to @JLattimer. He helped me figure our the error in my SOAP envelope.

    In my last post, I was trying to retrieve FullName from Crm 2011’s web services via PowerShell. Below is the code to do that.

    $xml = "<?xml version='1.0' encoding='utf-8'?>"
    $xml += "<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema'>"
    $xml += "<soap:Body>"
    $xml += "<Retrieve xmlns='http://schemas.microsoft.com/xrm/2011/Contracts/Services'>"
    $xml += "<entityName>contact</entityName>"
    $xml += "<id>12345678-1234-1234-1234-123456789012</id>"
    $xml += "<columnSet xmlns:q1='http://schemas.microsoft.com/xrm/2011/Contracts' xsi:type='q1:ColumnSet'>"
    $xml += "<q1:Columns xmlns:c='http://schemas.microsoft.com/2003/10/Serialization/Arrays'>"
    $xml += "<c:string>fullname</c:string>"
    $xml += "<c:string>telephone1</c:string>"
    $xml += "</q1:Columns>"
    $xml += "</columnSet>"
    $xml += "</Retrieve></soap:Body></soap:Envelope>"
    
    $url="http://crmserver.company.com/Organization/XRMServices/2011/Organization.svc/web"
    
    $http_request = New-Object -ComObject Msxml2.XMLHTTP
    $http_request.Open('POST', $url, $false)
    $http_request.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Retrieve");
    $http_request.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    $http_request.setRequestHeader("Content-Length", $xml.length);
    $http_request.send($xml);
    $http_request.responseText
    [ xml ]$results=$http_request.responseXML.xml
    $ns = New-Object Xml.XmlNamespaceManager $results.NameTable
    $ns.AddNamespace( "b", "http://schemas.microsoft.com/xrm/2011/Contracts" )
    $ns.AddNamespace( "c", "http://schemas.datacontract.org/2004/07/System.Collections.Generic" )
    $FullName=$results.selectSingleNode("//b:KeyValuePairOfstringanyType[c:key='fullname']/c:value/text()",$ns).Value
    $Telephone=$results.selectSingleNode("//b:KeyValuePairOfstringanyType[c:key='telephone1']/c:value/text()",$ns).Value
    

  • Using PowerShell to query CRM 2011 SOAP endpoint – help!

    I don’t know what I am doing wrong. All I am trying to do is to query  CRM 2011 via PowerShell and SOAP. The following PowerShell script should return the Contact’s full name, but I get nothing. Any ideas??

    Here is the PowerShell:

    $xml = "<?xml version='1.0' encoding='utf-8'?>"
    $xml += "<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema'>"
    $xml += "<soap:Body>"
    $xml += "<Retrieve xmlns='http://schemas.microsoft.com/xrm/2011/Contracts/Services'>"
    $xml += "<entityName>contact</entityName>"
    $xml += "<id>12345678-1234-1234-1234-123456789012</id>"
    $xml += "<columnSet xmlns:q1='http://schemas.microsoft.com/xrm/2011/Contracts' xsi:type='q1:ColumnSet'>"
    $xml += "<q1:Attributes>"
    $xml += "<q1:Attribute>fullname</q1:Attribute>"
    $xml += "<q1:Attribute>telephone1</q1:Attribute>"
    $xml += "</q1:Attributes>"
    $xml += "</columnSet>"
    $xml += "</Retrieve></soap:Body></soap:Envelope>"
    
    $url="http://crmserver.company.com/Organization/XRMServices/2011/Organization.svc/web"
    $http_request = New-Object -ComObject Msxml2.XMLHTTP
    $http_request.Open('POST', $url, $false)
    $http_request.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Retrieve");
    $http_request.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    $http_request.setRequestHeader("Content-Length", $xml.length);
    $http_request.send($xml);
    $http_request.responseText
    

    And here is the response:

    $http_request.responseText
    
    <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <s:Body><RetrieveResponse xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services">
    <RetrieveResult xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <a:Attributes xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic">
    <a:KeyValuePairOfstringanyType>
    <b:key>accountid</b:key>
    <b:value i:type="c:guid" xmlns:c="http://schemas.microsoft.com/2003/10/Serialization/">12345678-1234-1234-1234-123456789012</b:value>
    </a:KeyValuePairOfstringanyType>
    </a:Attributes>
    <a:EntityState i:nil="true"/><a:FormattedValues xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic"/>
    <a:Id>12345678-1234-1234-1234-123456789012</a:Id><a:LogicalName>account</a:LogicalName>
    <a:RelatedEntities xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic"/>
    </RetrieveResult>
    </RetrieveResponse>
    

    I just don’t know what I am doing wrong. Why won’t the code return “fullname”, it is in the columnSet.

    Update-2012-05-08:I have posted my question here, no answer yet

    Update2-2012-05-05: I have posted and received and answer here on stack overflow. Big thanks to @JLattimer


  • How to get the count of items returned in jQuery Autocomplete

    In this post I used jQuery to autocomplete an input box with results from CRM 2011. I wanted to give the number of results returned for the particular autocomplete query. I chose to use the autocomplete open: parameter to get the number of results returned, and I put that result in a div with an ID of “Count”

     

    open: function(event,ui){
    	    var len = $(this).autocomplete("widget").find( "li" ).length;
    	    var resultText='RESULTS';
    	    if (len==1) resultText='RESULT'
    	    $('#Count').text('FOUND '+len+' '+resultText);
    	  	},
    close: function(event,ui){
    	    $('#Count').text('ENTERPRISE SEARCH');
      	}
    
    

  • PowerShell script to copy a SharePoint Document Library to a Local Path

    I wanted to have a PowerShell script that downloaded A Document Library’s contents to a local folder. This is what I put together.

    Function JBM-SharePoint-CopyDocumentLibraryLocal {
    PARAM([Parameter(Mandatory=$true)]$SiteURL,
        [Parameter(Mandatory=$true)]$DocumentLibrary,
        [Parameter(Mandatory=$true)]$Destination)
    $spWeb = Get-SPWeb -Identity http://$SiteURL
    $list = $spWeb.Lists[$DocumentLibrary]
    foreach ($listItem in $list.Items)
    {
        $DestinationPath = $listItem.Url.replace("$DocumentLibrary","$Destination").Replace("/","\")
        write-host "Downloading $($listItem.Name) -> $DestinationPath"
    
        if (!(Test-Path -path $(Split-Path $DestinationPath -parent)))
        {
            write-host "Createing $(Split-Path $DestinationPath -parent)"
            $dest = New-Item $(Split-Path $DestinationPath -parent) -type directory
        }
        $binary=$spWeb.GetFile($listItem.Url).OpenBinary()
        $stream = New-Object System.IO.FileStream($DestinationPath), Create
        $writer = New-Object System.IO.BinaryWriter($stream)
        $writer.write($binary)
        $writer.Close()
    }
    $spWeb.Dispose()
    

  • Update DynDNS from the command line

    My router is not updating DynDNS correctly, so I wanted to use a simple cron job to do the same. Here is a simple BASH/Shell script to update DynDNS:

    IP=$(curl -s icanhazip.com)
    curl -v -k -u username:password “https://members.dyndns.org/nic/update?hostname=DNSHOSTNAME.dyndns.org&myip=$IP”


  • SharePoint 2010, Client OM, jQuery Autocomplete and BCS/External lists

    In this previous post, I used jQuery/SOAP/SPServices to access a SharePoint BCS list (an external list). I wanted to do the same thing using the Client Object Model (Some call it Clien OM? Or maybe CSOM?). Below is the code to access contact data in a BCS connection to Microsoft CRM 2011, and use it for autocomplete values. Things to note:

    1. You need to have a method tag in your CAML statement. This tells the list which “Operation” (as it is listed in SharePoint Designer) to use
    2. You need to use the “include method” with the executeQueryAsync method
    3. You need to include the ViewFields tags in the CAML statement, and I think they should match the include statement above
    $('#ContactsSearchTextbox').autocomplete({
        source: function( request, response ) {
    	    var CAMLQuery= "<View><Method Name='ContactReadList' /><Query><Where><Contains><FieldRef Name='FullName' /><Value Type='Text'>"+request.term+"</Value></Contains></Where></Query><ViewFields><FieldRef Name='FullName' /><FieldRef Name='ContactId' /><FieldRef Name='ParentCustomerIdName' /></ViewFields></View>";
    		var context = new SP.ClientContext.get_current();
    		var web = context.get_web();
    		var list = web.get_lists().getByTitle('Contacts');
    		var query = new SP.CamlQuery;
    		query.set_viewXml(CAMLQuery);
    		allItems = list.getItems(query);
    		context.load(allItems, 'Include(FullName,ContactId,ParentCustomerIdName)');
    		context.executeQueryAsync(function (){
    			var arrayOfResults = new Array();
    			var listItemEnumerator = allItems.getEnumerator(); 
    			while(listItemEnumerator.moveNext()) {
    				var oListItem = listItemEnumerator.get_current();
    				arrayOfResults.push(oListItem);
    			}
    			response($.map(arrayOfResults ,function( item ) {
    				return {
    				label: item.get_item('FullName') + ' (' + item.get_item('ParentCustomerIdName') + ')',
    				value: item.get_item('ContactId'),
    				id: item.get_item('FullName')
    				}
    			}));
    		},function(sender, args){
    		   alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
    		});        
    	},
    	minLength: 5,
    });