Edwin Daniel's profileMicrosoft technologies a...BlogLists Tools Help

Blog


    October 30

    Moving tempdb to a different location

    You'll probably see a lot of documentation on how to and why move the tempdb database on a different drive.  One of them is a TechNet paper on Working with the tempdb in SQL Server 2005.  The tempdb database is a system database which is similar to a user database except that it doesn't persist when SQL Server shuts down. SQL Server is the one managing the tempdb database but DBAs need to plan how to deploy the tempdb to achieve optimum performance.  Moving it to a different drive will take advantage of the parallel read-write capabilities of your disk subsystem.
     
    To move the tempdb database to a different drive, you need to use the ALTER DATABASE command
     
    use master
    go
    Alter database tempdb modify file (name = tempdev, filename = 'E:\Sqldata\tempdb.mdf')
    go
    Alter database tempdb modify file (name = templog, filename = 'F:\Sqldata\templog.ldf')
    go
     
    Make sure that you move the tempdb to a different disk subsystem, not just a different partition.  A complete step-by-step Microsoft Knowledge Base is available for other options in moving the tempdb database

    Cisco VPN Client on Windows Vista RC2 Build 5744

    One of the applications that I frequently use is the Cisco VPN client.  This enables us to connect to our data centers to remotely administer servers from just about anywhere in the world.  As far as my work is concerned, this application is a very important one.
     
    I installed Windows Vista Ultimate RC2 over the weekend and the very first application tht I installed was th Cisco VPN client software (apart from MSN and Yahoo! messenger).  I am using v4.8.01.0300 and started installing and to my surprise, the installation failed.  The worst part of it is, I could not even uninstall the application for reasons only Microsoft knew.  After much Google-ing and searching across forums, I chanced upon forum posts with people having the same problems as I have.  One suggested that I use v4.8.01.0410, which installs on WIndows Vista RC1 with no glitch.  This is also a beta version that Cisco came up with for the Vista platform.  I managed to download the specified version but since I could not uninstall the previous vesion, I had no other choice but to re-install Windows Vista from scratch so I can just install this version of the Cisco VPN client.  Finally, after installing the VPN client, I tested by connecting to our VPN, logged in to our servers and was successfully authenticated.  Installing a few more softwares like Acrobat Reader,  Windows Mobile Device Center (Windows Vista's version of ActiveSync) and my messenger clients.  I still have to test the other applications that I personally use for my presentations for Microsoft like Visual Studio and SQL Server 2005.  If they don't work fine, I don't have a choice but to run Windows XP as well on another partition and wait until every software I use is fullysupported in Windows Vista
    October 18

    Ez AD Scriptomatic

    I needed to create a script to create users and assign them to OUs and groups in Active Directory.  This is something that we needed to do on a regular basis that's why I wanted to do something to automate the task. I struggled on searching for the right book and webpage until I saw Ez AD Sciptomatic from Microsoft. ADSI Scriptomatic uses Active Directory Service Interfaces (ADSI), is the scripting technology used to manage Active Directory.  The tool generates a customizeable script for administrators to use to create Active Directory objects (OUs, users, groups, computers, etc.) which can be incorporated in any script.  In my case, I am integrating it in a script which reads an Excel spreadsheet containing the list of users and their information (firstname, lastname, login, email, etc.)
     
    Here's a sample script that the Ez AD Scriptomatic generates.  This one creates a user in Active Directory
     
    strContainer = ""
    strName = "test User"
    '***********************************************
    '*         Connect to a container              *
    '***********************************************
    Set objRootDSE = GetObject("
    LDAP://rootDSE")
    If strContainer = "" Then
      Set objContainer = GetObject("LDAP://" & _
        objRootDSE.Get("defaultNamingContext"))
    Else
      Set objContainer = GetObject("LDAP://" & strContainer & "," & _
        objRootDSE.Get("defaultNamingContext"))
    End If
    '***********************************************
    '*       End connect to a container            *
    '***********************************************
    Set objUser = objContainer.Create("user", "cn=" & strName)
    objUser.Put "sAMAccountName", strName
    objUser.SetInfo
    All that you need to do is set values for the strContainer and the strName variables.  strContainer will be the container on which this user will be created.  Since AD is heirarchical, the name of the container should also be heirarchical.  Let's say you want to create a user under the Finance OU, your strContainer value will be strContainer="ou=Finance".  If the user will be created under the Managers OU under the Finance OU, the value for strContainer will be strContainer="ou=Managers, ou=Finance".  The strName variable will contain he name you wat to assign to this user.
     
    Another sample script in creating a user in AD is shown below:
     
    Set objContainer = GetObject(LDAP://cn=Users,dc=yourdomain,dc=local)
    Set objUser = objContainer.Create("User", "cn=Fred Smith ")
    objUser.Put "samAccountName", "freds"
    objUser.SetInfo

    objUser.pwdLastSet = -1
    objUser.SetPassword "we12oi90"
    objUser.AccountDisabled = False

    objUser.SetInfo

    This approach is straight forward as you can assign more properties to an object upon creation. MSDN has a documentation for ADSI

    Your database is being accused

    When your database is corrupted or the database files are nowhere to be found, SQL Server 2000 normally marks it as suspect. In Enterprise Manager, you database will be marked Suspect when this happens.  I tried simulating this in SQL Server 2005 by deleting the database files so it will be marked suspect in SQL Server management Studio.  To my surprise, there was no SUSPECT tag in the database affected.  It appears as a normal database so any DBA would not be aware of any problems that this database could have encountered.  You will only know upon looking at the properties of the database. An error will be thrown:
     
    Database cannot be opened due to inaccessible files or insufficient memory or disk space.  See the SQL Server error log for details (Microsoft SQL Server, Error:945)
     
    I wonder why Microsoft removed this feature in Management Studio.  This is a very helpful tool for DBAs to quickly identify what is wrong with the database.
    October 13

    When the postman is not available, use pegions

    I have been trying to wait for management decision as to approving my workaround for using mail alerts for SQL Server.  A Microsoft KB article specifies that MAPI is not cluster-aware.  If you are using SQL Server, you know that SQL Server supports sending alerts through email.  Having read this article a few months back gave me no choice but to come up with a workaround. We have a clustered SQL Server with automated jobs running (backup, restore, log shipping, etc.). The email alerts we have configured back then do not work due to this limitation.  So I started working on a script that will be inserted in my SQL Server jobs.  The script is a step in the job which is called only if the previous job failed.  Although it is a bit troublesome as you have to do it manually on all SQL Server jobs, it's guaranteed to work on clustered SQL Servers.  Below is a script that I used to send email alerts using SMTP.
     
    Set objMessage = CreateObject("CDO.Message")
    objMessage.Subject = "Message Alert from SQL Admin"
    objMessage.From = "SQL
    Admin@myDomainName.net"
    objMessage.To = "SQLDBAs@myDomainName.net
    "
    objMessage.TextBody = "This is a sample message text. I am testing a VBScript to send email alerts from our SQL Server Cluster when the Dummy job failed"
     
    'Configuration information for the remote SMTP server.
    objMessage.Configuration.Fields.Item("
    http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
    'Name or IP of Remote SMTP Server
    objMessage.Configuration.Fields.Item("
    http://schemas.microsoft.com/cdo/configuration/smtpserver") = "smtp.myDomainName.net"
    'Server port number(typically 25)
    objMessage.Configuration.Fields.Item("
    http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
    objMessage.Configuration.Fields.Update
     
    objMessage.Send
    Set objMessage = Nothing

     
    I included this script as a step in my job and defined it as an Operating System command job script (I will be reusing this script for a lot of databases in a clustered instance that's why I decided to use a .VBS file being called by the job and passing some parameters like the database name). Tested it on my clustered SQL Servers by assigning a job that was configured to fail and I get flooded by emails.  Good thing I had my Outlook rules defined
    October 11

    SQL Server 2005 Instances Registry Entries

    I will be deploying a SQL Server 2005 on a production environment this time (after doing one for UAT environment a few weeks back).  Part of the deployment process is modifying the registry to incorporate some keys and parameter values for the AllowInProcess registry key.  This registry key is used to configure OLEDB providers for Distributed Queries in SQL Server.  One of the options to configure this is to use the sp_serveroption system stored procedure.  In our case, the application vendors did a more difficult approach - importing registry keys.  The thing about registry keys is that the source and the destination should have similar structures for a registry key to be imported properly.  What's worse is that the application vendors simply use SQL Server 2005 as a platform but their applications were still built using the concepts behind SQL Server 2000.  In order for me to successfully import the registry key, I looked at how SQL Server 2005 creates them when you install it on a Windows server.  In SQL Server 2000, an instance is defined in the registry under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server.  In SQL Server 2005 (which is also the same if you have SQL Server 2000 installed as another instance with SQL Server 2005), an instance is defined in the registry under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names.  This is where you can identify the instance name and the data value corresponding to your specific instance.  So for example, you installed SQL Server 2005 as the first named instance, testInstance, the data value for this will be MSSQL.1.  For the second named instance, testInstance2, the data value for this will be MSSQL.2 and so on and so forth.  So if you need to modify registry keys for SQL Server 2005, make sure you are modifying it for the correct instance by looking at this registry key group.
    October 10

    ASP.NET 2.0 Profiles

    The Profile provider in ASP.NET stores and retrieves information about our site’s users.  It helps keep track of information from those who visit your site - whether they are anonymous or authenticated users.  By default, the Profile provider uses a SQL Server 2005 Express as a data source to store these information although you can always customize the Profile provider to use some data sources other than SQL Server.  Right out-of-the-box, web developers can simply use the Profile provider without having to do a lot of coding. The only thing that you need to do is to provide a definition of the user profile in the web.config.  Below is a sample web.config file which defines a Profile property named visitorAliasName with a default value of guest and which allows anonymous users to use it. And since we are allowing anonymous users to make use of our Profile provider, we added another element in the web.config file named anonymousIdentification and set it's enabled property value to true.
     
     <?xml version="1.0"?>
    <
    configuration
    >
         <
    appSettings/> 

         <
    connectionStrings/> 
         
         <system.web>
                   <
    compilation debug="false" strict="false" explicit="true" /> 

                   <
    authentication mode="Forms" /> 

                   <

    anonymousIdentification enabled="true"/>
                   <
    profile>
                         <
    properties
    >
                             <
    add name="visitorAliasName" defaultValue="guest" allowAnonymous="true"
    />
                         </
    properties

                   </
    profile

         </
    system.web>

    </
    configuration>
     
    Now that we have defined the Profile provider with one property, we can start using it in our ASP.NET 2.0 page.  One thing about the Profile provider is that once it is defined in the web.config file, it provides the developers a type-safe onject which is available in the IntelliSense feature in Visual Studio 2005/Visual Web Developer Express. Let's start by adding a text box and a button to set the profile.   Below is the code for an ASP.NET 2.0 page that uses the Profile provider:
     
    <script runat="server">
      
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
       If Not Page.IsPostBack Then
         TextBox1.Text = Profile.visitorAliasName
       End If
    End Sub
    Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
       Profile.visitorAliasName = TextBox1.Text
    End Sub
     
    </script>edwin
     

    <
    html xmlns="http://www.w3.org/1999/xhtml" >
    <
    head runat="server">
       <title>Untitled Page</title>
    </
    head>
    <
    body>
        <form id="form1" runat="server">
         <div>
        Enter Alias:
        <br />
        <br />
        <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox><br />
        <asp:Button ID="Button1" runat="server" Text="Save" /></div>
        </form>
    </
    body>
    </
    html>
     
    If you look at the codes, there is nothing in there that shows you where a database connection nor an implicit insert to a database is being made.  During the first call to this page, ASP.NET 2.0 does all of the background processing for you - database and objects creation and record insertions. Of course, this will only work if you have a SQL Server 2005 Express instance running on the server that hosts your ASP.NET 2.0 application.  The above sample creates a profile and displays the visitorAliasName property value in the textbox.  When you change the value in the textbox, it assigns the new value to the visitorAliasName property
    October 04

    Reading information from Excel using VBScript

    This is a continuation of the script I was writing to audit all of the machines/servers in my domain using WMI and VBScript.  The next part of that is having an Excel spreadsheet that contains the list of all your computers/servers in the domain. I don't want to manually run the script I created on each machine.  So, this is a continuation of the script I was creating.  It uses VBScript to read from my Excel spreadsheet.  Once I can read from the Excel spreasheet, I can then pass the values as parameters to my script that does the auditing.

    Here's my script on reading an Excel spreadsheet. This assumes that the script and the Excel file are in the same folder. My Excel spreadsheet has this format:

    IP Address            MachineName
    ------------------------------------------------
    192.168.1.1            PC1
    192.168.1.2            PC2
    192.168.1.3            PC3

    On Error Resume Next

    Dim strIPvalue   'Value extracted from each cell
    Dim intLoopCount, dir, objExcel, objWorkbook
    Set objExcel = CreateObject("Excel.Application")

    'Gets the directory where our script is running from
    dir = CreateObject("Scripting.FileSystemObject").GetParentFolderName(Wscript.ScriptFullName)

    'Open our EXCEL file
    Set objWorkbook = objExcel.Workbooks.Open(dir & "\serverList.xls")

    'This is to skip the header row, and then the blank row below.
    'If no header and row, this value should be 1
    intLoopCount = 3

    Do While Not IsEmpty(objExcel.Cells(loopCount, 1).Value)
      strIPvalue = objExcel.Cells(intLoopCount,1).Value
            
      'Displays the IP Address in a message box
      Wscript.Echo strIPvalue
           
    intLoopCount = intLoopCount + 1
       
    Loop

    objExcel.Workbooks.Close
    objExcel.quit
    objExcel = Empty
    objWorkbook = Empty

    October 02

    using WMI and VBScript to audit

    Auditing hardware and software for all machines in your domain can be time consuming.  For small organizations, it may be a bit manageable provided that you already have a checklist of items that you need to look at for auditing purposes.  CPU, disk, RAM, OS, service pack versions, etc. are just a few of those things you would like to take into account when doing auditing and inventory.  For larger organizations, this could be a big challenge.  Since I work for an organization that provides IT services for clients, we are required to maintain server information for auditing and inventory purposes. Imagine me doing this for almost 200+ Windows 2000 and Windows 2003 servers in multiple domains, not to mention Windows XP workstations. Being a lazy guy as I am, I wrote a script to simply automate this task. It uses VBScript and WMI to retrieve hardware, software and operating system information from computers in the domain.  This requires administrative rights on the machine where this script is executed.  It generates a text file (with filename servername_yyyymmdd_Audit.txt) which contains the information retrieved by the script.  This is my list of information, you can always generate a lot more information by referring to the WMI SDK.   Simply replace the value in the strComputer variable to the name/IP address of the computer you wish to audit

     'Script to change a filename using timestamps
    strPath = "D:\test\"   'Change the path to appropriate value
    strMonth = DatePart("m", Now())
    strDay = DatePart("d",Now())

    if Len(strMonth)=1 then
     strMonth = "0" & strMonth
    else
     strMonth = strMonth
    end if


    if Len(strDay)=1 then
     strDay = "0" & strDay
    else
     strDay = strDay
    end if


    strFileName = DatePart("yyyy",Now()) & strMonth & strDay
    strFileName = Replace(strFileName,":","")
    '=================================================================================

    'Variable Declarations
    Const ForAppending = 8

    '===============================================================================
    'Main Body
    On Error Resume Next


    'CompName
    strComputer = "localhost"
    Set objWMIService = GetObject("winmgmts:" _
         & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
    '===============================================================================

    '================================================================
    'For INTERNET EXPLORER
    Dim strIE
    Set objWMIService2 = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2\Applications\MicrosoftIE")
    Set colIESettings = objWMIService2.ExecQuery("Select * from MicrosoftIE_Summary")
    For Each strIESetting in colIESettings
        strIE= "          INTERNET EXPLORER: " & strIESetting.Name & " v" & strIESetting.Version & VBCRLF
    Next


    'Get Operation System & Processor Information
    Set colItems = objWMIService.ExecQuery("Select * from Win32_Processor",,48)
    For Each objItem in colItems
         CompName = objItem.SystemName
    Next

    Set objFSO = CreateObject("Scripting.FileSystemObject")
    if objFSO.FileExists(strPath & CompName & "_" & strFileName & "_Audit.txt") then
     WScript.Quit
    end if

    'Set the file location to collect the data
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    Set objTextFile = objFSO.OpenTextFile(strPath & CompName & "_" & strFileName & "_Audit.txt", ForAppending, True)

    ''==============================================================
    'Print HEADER
    objTextFile.Write "================================================================" & VBCRLF & VBCRLF
    objTextFile.Write "                     SERVER RESOURCE AUDIT REPORT               " & VBCRLF
    objTextFile.Write "                     DATE:  " &    FormatDateTime(Now(),1)   & "                   " & VBCRLF
    objTextFile.Write "                     TIME:  " &    FormatDateTime(Now(),3)   & "                   " & VBCRLF & VBCRLF
    objTextFile.Write "================================================================" & VBCRLF & VBCRLF & VBCRLF & VBCRLF & VBCRLF


    objTextFile.Write "COMPUTER" & VBCRLF
    '==============================================================
    'Get OPERATING SYSTEM & Processor Information
    Set colItems = objWMIService.ExecQuery("Select * from Win32_Processor",,48)
    For Each objItem in colItems
         objTextFile.Write "          COMPUTER NAME: " &  objItem.SystemName & VBCRLF
         objTextFile.Write "          PROCESSOR: " &  objItem.Name & VBCRLF
    Next

    '================================================================
    'Get DOMAIN NAME information
    Set colItems = objWMIService.ExecQuery("Select * from Win32_NTDomain")

    For Each objItem in colItems
        objTextFile.Write "          DOMAIN NAME: " &  objItem.DomainName & VBCRLF
    Next

    '================================================================
    'Get OS Information
    Set colSettings = objWMIService.ExecQuery("SELECT * FROM Win32_OperatingSystem")
    For Each objOperatingSystem in colSettings
     objTextFile.Write "          OPERATING SYSTEM: " & objOperatingSystem.Name & VBCRLF
     objTextFile.Write "          VERSION: " & objOperatingSystem.Version & VBCRLF
     objTextFile.Write "          SERVICE PACK: " & objOperatingSystem.ServicePackMajorVersion & "." & objOperatingSystem.ServicePackMinorVersion & VBCRLF
    Next
     objTextFile.Write strIE & VBCRLF & VBCRLF & VBCRLF & VBCRLF

    objTextFile.Write "MOTHERBOARD" & VBCRLF

    '===============================================================
    'Get Main Board Information
    Set colItems = objWMIService.ExecQuery("Select * from Win32_BaseBoard",,48)
    For Each objItem in colItems
         objTextFile.Write "          MAINBOARD MANUFACTURER: "  & objItem.Manufacturer & VBCRLF
         objTextFile.Write "          MAINBOARD PRODUCT: " &  objItem.Product & VBCRLF
    Next

    '================================================================
    'Get BIOS Information
    Set colItems = objWMIService.ExecQuery("Select * from Win32_BIOS",,48)
    For Each objItem in colItems
         objTextFile.Write "          BIOS MANUFACTURER: " & objItem.Manufacturer & VBCRLF
         objTextFile.Write "          BIOS VERSION: " &  objItem.Version & VBCRLF & VBCRLF & VBCRLF & VBCRLF & VBCRLF
    Next

    objTextFile.Write "MEMORY" & VBCRLF

    '===================================================================
    'Get Total Physical memory
    Set colSettings = objWMIService.ExecQuery("Select * from Win32_ComputerSystem")
    For Each objComputer in colSettings
         objTextFile.Write "          TOTAL PHYSICAL RAM: " & Round((objComputer.TotalPhysicalMemory/1000000000),4) & " GB" & VBCRLF
    Next

    objTextFile.Write " " & VBCRLF & VBCRLF & VBCRLF & VBCRLF & "PARTITIONS" & VBCRLF

    '===================================================================
    'Get Logical Disk Size and Partition Information
    Set colDisks = objWMIService.ExecQuery("Select * from Win32_LogicalDisk Where DriveType = 3")
    For Each objDisk in colDisks
        intFreeSpace = objDisk.FreeSpace
        intTotalSpace = objDisk.Size
        pctFreeSpace = intFreeSpace / intTotalSpace
        objTextFile.Write "          DISK " & objDisk.DeviceID & " (" & objDisk.FileSystem & ") " &  Round((objDisk.Size/1000000000),4) & " GB ("& Round((intFreeSpace/1000000000)*1.024,4) & " GB Free Space)" & VBCRLF
    Next

    objTextFile.Write " " & VBCRLF & VBCRLF & VBCRLF & VBCRLF & "NETWORK" & VBCRLF


    '====================================================================
    'Get NETWORK ADAPTERS information
    Dim strIP, strSubnet, strDescription

    Set colNicConfigs = objWMIService.ExecQuery("SELECT * FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled = True")
     
    For Each objNicConfig In colNicConfigs
           'Assign description values to variable
           strDescription=objNicConfig.Description

           For Each strIPAddress In objNicConfig.IPAddress
     'Assign IP Address to variable
     strIP=strIPAddress 

       For Each strIPSubnet In objNicConfig.IPSubnet
          'Assign Subnet to variable
          strSubnet = strIPSubnet
       Next
     
     objTextFile.Write "          NETWORK ADAPTER: " & strDescription & VBCRLF
      objTextFile.Write "          IP ADDRESS: " & strIP & VBCRLF
        objTextFile.Write "          SUBNET MASK: " & strSubnet & VBCRLF & VBCRLF

           Next

    Next

    Set colNicConfigs =NOTHING


    'Close text file after writing logs

    objTextFile.Write VbCrLf
    objTextFile.Close

    'Clean Up

    SET colIESettings=NOTHING
    SET colItems=NOTHING
    SET colSettings=NOTHING
    SET colDisks=NOTHING
    SET AdapterSet=NOTHING
    SET objWMIService=NOTHING
    SET objWMIService2=NOTHING
    SET objFSO=NOTHING
    SET objTextFile=NOTHING

    If you are dealing with an organization with more than 50 computers and servers, it would still be tiresome to manually execute this script on each machine/server.  A better way to do it is to have an Excel spreadsheet that contains the machine names/IP addresses of all the computers in your domain. read through the list and generate the text files based on that list.  In my next blog entry, I will start with reading an Excel spreadsheet using VBScript and continue on to incorporate this script.