Sunday, November 13, 2016

Parsing MDX by Dynamic Query

The following script is an example of parsing MDX by Dynamic query and storing the result in a temporary table.

USE MyDataBase
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET FMTONLY OFF
GO

SET NOCOUNT ON
GO

PRINT 'Date: ' + CONVERT(VARCHAR(24),GETDATE(),13) + ' Server Name: ' + @@servername + ' Service Name: ' + @@servicename;
GO

IF EXISTS
(SELECT *
FROM sys.objects
WHERE name = 'Last_Period_Revenue_MA')
DROP PROC dbo.Last_Period_Revenue_MA;

PRINT 'DROP PROC dbo.Last_Period_Revenue_MA';
GO

PRINT 'CREATE PROC dbo.Last_Period_Revenue_MA';
GO

CREATE PROC dbo.Last_Period_Revenue_MA
/************************************************************************************************************************************
Description:
Change Log

# Date Who Change
*************************************************************************************************************************************/
@CustomerCode NVARCHAR(255)
,@Period INT = -12
AS
BEGIN
BEGIN TRY

DECLARE @StrMDX AS NVARCHAR(MAX)
,@BOMDate AS SMALLDATETIME
,@DateFr AS SMALLDATETIME
,@DateTo AS SMALLDATETIME
,@StrSQL AS NVARCHAR(MAX)
,@Amount FLOAT;

SET @BOMDate = DATEADD(d,-DAY(GETDATE())+1, GETDATE());
SET @DateFr = DATEADD(m, @Period, @BOMDate);
SET @DateTo = DATEADD (d, -1, @BOMDate);
 
CREATE TABLE #MSCRM_Roll_DataSet1 (Amount FLOAT NULL);

CREATE TABLE #MSCRM_Roll_DataSet2 ( CustomerType NVARCHAR(255) NULL
,CustomerName VARCHAR(255) NULL
,CustomerSite VARCHAR(255) NULL
,FinancialYearDaily NVARCHAR(255) NULL
,FinancialPeriodDaily NVARCHAR(255) NULL
,CalendarDateDaily NVARCHAR(255) NULL
,ExclAmount FLOAT NULL);

CREATE TABLE #MSCRM_Roll_DataSet3 ( MthName NVARCHAR(20) NULL
,YrMth NVARCHAR(10) NULL
,ExclAmount FLOAT NULL);

SET @StrSQL = '([Calendar].[' +
right('0'+CAST(DAY(@DateFr) AS nvarchar),2)
+ '/'+ right('0'+CAST(MONTH(@DateFr) AS nvarchar),2)
+ '/'+ CAST(YEAR(@DateFr) AS nvarchar) + '] : [Calendar].['
+ right('0'+CAST(DAY(@DateTo) AS nvarchar),2)
+ '/'+ right('0'+CAST(MONTH(@DateTo) AS nvarchar),2)
+ '/'+ CAST(YEAR(@DateTo) AS nvarchar) + '])';

SET @StrMDX = 'INSERT INTO #MSCRM_Roll_DataSet1 SELECT * FROM OPENQUERY ([PROD_AS], ' + CHAR(39) +
'SELECT { [Measures].[Exclusive Amount] } ON COLUMNS
FROM ( SELECT ( { [Customer].[Customers].[CUSTOMER NAME].[' + @CustomerCode +'] } ) ON COLUMNS
FROM ( SELECT ( { ' + @StrSQL + ' } ) ON COLUMNS FROM [Sales])) ' + CHAR(39) + ')';

EXEC sp_executesql @StrMDX;

SET @Amount = (SELECT Amount FROM #MSCRM_Roll_DataSet1)
SET @StrMDX = 'INSERT INTO #MSCRM_Roll_DataSet2 SELECT * FROM OPENQUERY ([PROD_AS], ' + CHAR(39) +
'SELECT NON EMPTY { [Measures].[Exclusive Amount] } ON COLUMNS
,NON EMPTY { ([Customer].[Customers].[Ref Customer Site].ALLMEMBERS
* [Calendar].[Calendar Date Daily].ALLMEMBERS ) } ON ROWS
FROM ( SELECT ( { [Customer].[Customers].[CUSTOMER NAME].&[' + @CustomerCode +'] } ) ON COLUMNS
FROM ( SELECT ( { ' + @StrSQL + ' } ) ON COLUMNS FROM [Sales])) ' + CHAR(39) + ')';

EXEC sp_executesql @StrMDX;

INSERT INTO #MSCRM_Roll_DataSet3

SELECT MAX(DATENAME(m, CAST(SUBSTRING(CalendarDateDaily,7,4)
+ '-' + SUBSTRING(CalendarDateDaily,4,2)
+ '-' + SUBSTRING(CalendarDateDaily,1,2) AS SMALLDATETIME)))
, MAX(SUBSTRING(CalendarDateDaily,7,4)
+ '-' + SUBSTRING(CalendarDateDaily,4,2))
, SUM (ExclAmount) from #MSCRM_Roll_DataSet2
GROUP BY SUBSTRING(CalendarDateDaily,7,4)
+ '-' + SUBSTRING(CalendarDateDaily,4,2);

WHILE @DateFr < @DateTo
BEGIN

INSERT INTO #MSCRM_Roll_DataSet3
SELECT DATENAME(m,@DateFr)
, DATENAME(year,@DateFr)
+ '-' + right('0'+CAST(DATEPART(MONTH,@DateFr)as NVARCHAR(2)),2) , 0;

SET @DateFr = DATEADD(m,1,@DateFr);

END;

SELECT MthName
,MthName + ' ' + SUBSTRING(YrMth,3 , 2) MthYr
,YrMth
,ExclAmount
FROM #MSCRM_Roll_DataSet3;

DROP TABLE #MSCRM_Roll_DataSet1;
DROP TABLE #MSCRM_Roll_DataSet2;
DROP TABLE #MSCRM_Roll_DataSet3;

END TRY
BEGIN CATCH

SELECT ERROR_NUMBER() AS ErrorNumber
,ERROR_SEVERITY() AS ErrorSeverity
,ERROR_STATE() AS ErrorState
,ERROR_PROCEDURE() AS ErrorProcedure
,ERROR_LINE() AS ErrorLine
,ERROR_MESSAGE() AS ErrorMessage;

END CATCH

END
GO
/***************************************************************************************
Call Sample:

EXEC dbo.Last_Period_Revenue_MA
'BAUL4021 - Baulderstone Queensland Pty Ltd'
,-24
****************************************************************************************/
 
If you find this script useful please donate generously.

Sunday, October 16, 2016

SSRS Reports Audit

The following T-SQL User Defined Functions running against Reporting Services database serve the following purposes

•        Post deployment audit of reports and data sets

•        Analysis of report dependencies across one or multiple reports
 
PRINT 'Date: ' + CONVERT(VARCHAR(24),GETDATE(),13) + '   Server Name: ' + @@servername + '   Service Name: ' + @@servicename
GO 
 
IF EXISTS
      (SELECT *
   FROM sys.objects
   WHERE name = 'Extract_Report_Info')

  DROP FUNCTION dbo.Extract_Report_Info
  PRINT 'DROP FUNCTION dbo.Extract_Report_Info'
GO

PRINT 'CREATE FUNCTION dbo.Extract_Report_Info'
GO

CREATE FUNCTION dbo.Extract_Report_Info(
/*****************************************************************************
Purpose: Extracts the contents of all Reports, Data Sources, Report Parts and Shared Datasets (Types 2,5,7, & 8) from the database

Change Log

 #    Date       Who  Change
******************************************************************************/
 @rptName NVARCHAR(255)
 ,@rptPath NVARCHAR(255))
RETURNS @tempTBL TABLE (
   ItemID NVARCHAR(255)
   ,Name NVARCHAR(255)
   ,[Path] NVARCHAR(MAX)
   ,[Type] NVARCHAR(255)
   ,TypeDescription NVARCHAR(MAX)
   ,Content varbinary(MAX)
   ,ContentVarchar NVARCHAR(MAX)
   ,ContentXML xml
)
AS
BEGIN

 --The first CTE gets the content AS a varbinary(MAX)
 --as well AS the other important columns for all reports,
 --data sources and shared datasets.

 WITH ItemContentBinaries AS
 (
  SELECT
  ItemID
  ,Name
  ,[Path]
  ,[Type]
  ,CASE Type
    WHEN 2 THEN 'Report'
    WHEN 5 THEN 'Data Source'
    WHEN 7 THEN 'Report Part'
    WHEN 8 THEN 'Shared Dataset'
    ELSE 'Other'
  END AS TypeDescription
  ,CONVERT(varbinary(MAX),Content) AS Content
  --FROM OPENROWSET('SQLNCLI', 'Server=COA-DARC-SQL05\DEV_INTEGRATION;Trusted_Connection=yes;',
  --    'SELECT * FROM ReportServer.dbo.Catalog')
  FROM ReportServer.dbo.Catalog
  WHERE Type IN (2,5,7,8)
 ),

 --The second CTE strips off the BOM if it exists...

 ItemContentNoBOM AS
 (
  SELECT
  ItemID
  ,Name
  ,[Path]
  ,[Type]
  ,TypeDescription
  ,CASE
    WHEN LEFT(Content,3) = 0xEFBBBF
   THEN CONVERT(varbinary(MAX),SUBSTRING(Content,4,LEN(Content)))
   ELSE
   Content
  END AS Content
  FROM ItemContentBinaries
 )

 --The outer query gets the content in its varbinary, varchar and xml representations...

 INSERT INTO @tempTBL
 SELECT
    ItemID
   ,Name
   ,[Path]
   ,[Type]
   ,TypeDescription
   ,Content           --varbinary
   ,CONVERT(varCHAR(MAX),Content) AS ContentVarchar --varchar
   ,CONVERT(xml,Content) AS ContentXML    --xml
 FROM ItemContentNoBOM
 WHERE (ISNULL(@rptName,'') = ''
  OR
  name LIKE '%' + @rptName + '%')
  AND
  (ISNULL(@rptPath,'') = ''
  OR
  [Path] LIKE '%' + @rptPath + '%')
 RETURN
END
GO
/*******************************************************************
Test Sample:
 SELECT * FROM dbo.Extract_Report_Info('{Report Name},'{Report Path}')
 SELECT * FROM dbo.Extract_Report_Info('{Report Name},',NULL)
 SELECT * FROM dbo.Extract_Report_Info(NULL,Null)
 ********************************************************************/
----------------------------------------------------------------------------------------------------------
SET ANSI_NULLS ON
GOSET QUOTED_IDENTIFIER ON
GOPRINT 'Date: ' + CONVERT(VARCHAR(24),GETDATE(),13) + '   Server Name: ' + @@servername + '   Service Name: ' + @@servicename
GOIF EXISTS
      (SELECT *
   FROM sys.objects
   WHERE name = 'Extract_Dataset_Commands')
  DROP FUNCTION dbo.Extract_Dataset_Commands
  PRINT 'DROP FUNCTION dbo.Extract_Dataset_Commands'
GOPRINT 'CREATE FUNCTION dbo.Extract_Dataset_Commands'
GOCREATE FUNCTION dbo.Extract_Dataset_Commands(
/****************************************************************************
Purpose : extracts the actual commands used for the each dataSET in each Report or Shared Dataset.Change Log
 #    Date       Who  Change
*****************************************************************************/
  @rptName NVARCHAR(255)
 ,@rptPath NVARCHAR(255)
 ,@cmdText NVARCHAR(255))
RETURNS @tempTBL TABLE ( ItemID NVARCHAR(255)
      ,Name NVARCHAR(255)
      ,[Path] NVARCHAR(MAX)
      ,[Type] NVARCHAR(255)
      ,TypeDescription NVARCHAR(MAX)
      ,ContentXML xml
      ,CommandType NVARCHAR(MAX)
      ,CommandText NVARCHAR(MAX)
)
AS
BEGIN
 --The first CTE gets the content AS a varbinary(MAX)
 --as well AS the other important columns for all reports,
 --data sources and shared datasets.
 WITH ItemContentBinaries AS
 (
  SELECT
  ItemID
  ,Name
  ,[Path]
  ,[Type]
  ,CASE Type
    WHEN 2 THEN 'Report'
    WHEN 5 THEN 'Data Source'
    WHEN 7 THEN 'Report Part'
    WHEN 8 THEN 'Shared Dataset'
    ELSE 'Other'
  END AS TypeDescription
  ,CONVERT(varbinary(MAX),Content) AS Content
  --FROM OPENROWSET('SQLNCLI', 'Server=COA-DARC-SQL05\DEV_INTEGRATION;Trusted_Connection=yes;',
   --   'SELECT * FROM ReportServer.dbo.Catalog')
  FROM ReportServer.dbo.Catalog
  WHERE Type IN (2,5,7,8)
 ),
 --The second CTE strips off the BOM if it exists...
 ItemContentNoBOM AS
 (
  SELECT
  ItemID
  ,Name
  ,[Path]
  ,[Type]
  ,TypeDescription
  ,CASE
    WHEN LEFT(Content,3) = 0xEFBBBF
   THEN CONVERT(varbinary(MAX),SUBSTRING(Content,4,LEN(Content)))
    ELSE
   Content
  END AS Content
  FROM ItemContentBinaries
 )
 --The old outer query is now a CTE to get the content in its xml form only...
 ,ItemContentXML AS
 (
  SELECT  ItemID
   ,Name
   ,[Path]
   ,[Type]
   ,TypeDescription
   ,CONVERT(xml,Content) AS ContentXML
 FROM ItemContentNoBOM
 )
 --now use the XML data type to extract the queries, and their command types and text....
 INSERT INTO @tempTBL
 SELECT
   ItemID
  ,Name
  ,[Path]
  ,[Type]
  ,TypeDescription
  ,ContentXML
  ,ISNULL(Query.value('(./*:CommandType/text())[1]','NVARCHAR(1024)'),'Query') AS CommandType
  ,Query.value('(./*:CommandText/text())[1]','NVARCHAR(MAX)') AS CommandText
 FROM ItemContentXML
 --Get all the Query elements (The "*:" ignores any xml namespaces)
 CROSS APPLY ItemContentXML.ContentXML.nodes('//*:Query') Queries(Query)
 WHERE (ISNULL(@rptName,'') = ''
  OR
  name LIKE '%' + @rptName + '%')
  AND
  (ISNULL(@rptPath,'') = ''
  OR
  [Path] LIKE '%' + @rptPath + '%')
  AND
  (ISNULL(@cmdText,'') = ''
  OR
  Query.value('(./*:CommandText/text())[1]','NVARCHAR(MAX)') LIKE '%' + @cmdText + '%')
 RETURN
END
GO
/*****************************************************************************
Test Sample:
 SELECT * FROM dbo.Extract_Dataset_Commands(‘{Report Name}','{Report Path}',NULL)
 SELECT * FROM dbo.Extract_Dataset_Commands('{Report Name}',NULL, 'select')
 SELECT * FROM dbo.Extract_Dataset_Commands(NULL,'{Report Path}’','SELECT')
*****************************************************************************/

 SET ANSI_NULLS ON
GOSET QUOTED_IDENTIFIER ON
GOPRINT 'Date: ' + CONVERT(VARCHAR(24),GETDATE(),13) + '   Server Name: ' + @@servername + '   Service Name: ' + @@servicename
GOIF EXISTS(    SELECT VIEW_DEFINITION FROM INFORMATION_SCHEMA.VIEWS
            WHERE VIEW_DEFINITION LIKE '%dbo.Shared_Data_Sources%')
BEGIN
    DROP VIEW dbo.Shared_Data_Sources
    PRINT 'DROP VIEW dbo.Shared_Data_Sources'
END
GOPRINT 'CREATE VIEW dbo.Shared_Data_Sources'
GO/************************************************************************************************************************************
Reports Audit - Extract Shared Data SourcesChange Log
 #    Date       Who  Change
*************************************************************************************************************************************/
CREATE VIEW dbo.Shared_Data_Sources
AS
    WITH XMLNAMESPACES
        -- XML namespace def must be the first in with clause.
        (
        DEFAULT
        'http://schemas.microsoft.com/sqlserver/reporting/2006/03/reportdatasource'
        ,
        'http://schemas.microsoft.com/SQLServer/reporting/reportdesigner'
        AS rd
        )
        ,SDS
    AS (
        SELECT SDS.NAME AS SharedDsName
            ,SDS.[Path]
            ,CONVERT(XML, CONVERT(VARBINARY(max), content)) AS DEF
        FROM dbo.[Catalog] AS SDS
        WHERE SDS.Type = 5
        ) -- 5 = Shared Datasource
    SELECT CON.[Path]
        ,CON.SharedDsName
        ,CON.ConnString
    FROM (
        SELECT SDS.[Path]
            ,SDS.SharedDsName
            ,DSN.value('ConnectString[1]', 'varchar(150)') AS
            ConnString
        FROM SDS
        CROSS APPLY SDS.DEF.nodes('/DataSourceDefinition') AS R(DSN
            )
        ) AS CON
GOSET ANSI_NULLS ON
GOSET QUOTED_IDENTIFIER ON
GOPRINT 'Date: ' + CONVERT(VARCHAR(24),GETDATE(),13) + '   Server Name: ' + @@servername + '   Service Name: ' + @@servicename
GOIF EXISTS(    SELECT VIEW_DEFINITION FROM INFORMATION_SCHEMA.VIEWS
            WHERE VIEW_DEFINITION LIKE '%dbo.Report_Data_Sources%')
BEGIN
    DROP VIEW dbo.Report_Data_Sources
    PRINT 'DROP VIEW dbo.Report_Data_Sources'
END
GOPRINT 'CREATE VIEW dbo.Report_Data_Sources'
GO/************************************************************************************************************************************
Reports Audit - Extract Report Data SourcesChange Log
 #    Date       Who  Change
*************************************************************************************************************************************/
CREATE VIEW dbo.Report_Data_Sources
AS
SELECT   C.ItemID
        ,C.Name
        ,C.Path
        ,C.Type
        ,CASE C.Type  WHEN 2 THEN 'Report'
                    WHEN 5 THEN 'Data Source'
                    WHEN 7 THEN 'Report Part'
                    WHEN 8 THEN 'Shared Dataset'
                    ELSE 'Other' END AS TypeDescription
        , CONVERT(varbinary(MAX), C.[Content]) AS Content
        , D.Name DataSourceName
FROM     dbo.Catalog C LEFT JOIN
         dbo.DataSource D
         ON C.ItemID = D.ItemID
WHERE  (C.Type IN (2, 5, 7, 8))
GO

It allows:·        Extract from shared data source the connection string for each report
SELECT R.ItemID
             ,R.Name
             ,R.Path
             ,R.Type
             ,R.TypeDescription
             ,R.[Content]
             ,R.DataSourceName
FROM     dbo.Report_Data_Sources R LEFT OUTER JOIN
         dbo.Shared_Data_Sources S
             ON R.DataSourceName = S.SharedDsName
WHERE R.Name = '{Report Name}'·        Identify reports which have no shared data sources attachedSELECT R.ItemID
             ,R.Name
             ,R.Path
             ,R.Type
             ,R.TypeDescription
             ,R.[Content]
             ,R.DataSourceName
FROM     dbo.Report_Data_Sources R LEFT OUTER JOIN
         dbo.Shared_Data_Sources S
             ON R.DataSourceName = S.SharedDsName
WHERE DataSourceName IS NULL

The above udf’s can be executed from the following PowerShell Script: #Query a SQL database
 $SQLServer = "DB\INSTANCE";
 $SQLDBName = "ReportServer";
 $rptName = "Report Name";
 $rptPath = "Report Path";
 $SqlQuery = "SELECT * FROM [dbo].[Extract_Report_Info]('" + $rptName + "'," + "'" + $rptPath + "');";
 $smtp = "smtp address";
 $From = "noreply email address";
 $To = "destination email address";
 $connectionName = "Extract_RS_Info";
 $commandTimeout = 15; clear;
 $SqlQuery; Function Extract_RS_Info {  Param (  $server = $SQLServer
    ,$database = $SQLDBName
    ,$smtp
    ,$From
    ,$To
    ,$rptName
    ,$rptPath
    ,$SqlQuery
    ,$connectionName
    ,$commandTimeout)
  Try{
   $conn =new-object ('System.Data.SqlClient.SqlConnection')
   $connString = "Server=$server;Integrated Security=SSPI;Database=$database;Application Name=$connectionName"
   $conn.ConnectionString = $connString
   Write-Debug ("Function: Query-SQL: $server $database")
   if (test-path variable:\conn) {
    $conn.close()
   } else {
    $conn =new-object ('System.Data.SqlClient.SqlConnection')
   }
   $conn.Open()
   $sqlCmd =New-Object System.Data.SqlClient.SqlCommand
   $sqlCmd.CommandTimeout = $CommandTimeout
   $sqlCmd.CommandText = $SqlQuery
   $sqlCmd.Connection = $conn
   $data = $sqlCmd.ExecuteReader()
   while ($data.read() -eq $true) {
    $max = $data.FieldCount -1
    $obj =New-Object Object
    For ($i = 0; $i -le $max; $i++) {
     $name = $data.GetName($i)
     if ($name.length -eq 0) {
     $name = "field$i"
    }
    $obj |Add-Member Noteproperty $name -value $data.GetValue($i) -Force
   }
   $obj
   }
   $conn.close()
   $conn = $null
  } Catch {
   $ErrorMessage = $_.Exception.Message;
   $FailedItem = $_.Exception.ItemName;
   $body = "We failed to read file $FailedItem. The error message was $ErrorMessage";
   Send-MailMessage -From $From -To $To -Subject "Extract_RS_Info.ps1 failed!" -SmtpServer $smtp -Body $body;
   Break
  }
 } Extract_RS_Info $SQLServer $SQLDBName $smtp $From $To $rptName $rptPath $SqlQuery $connectionName $commandTimeout; $str = "*" * 80;
 write-host $str;
 $SqlQuery = "SELECT * FROM [dbo].[Extract_Dataset_Commands]('" + $rptName + "','" + $rptPath + "','" + $cmdText + "');";
 $SqlQuery; Extract_RS_Info $SQLServer $SQLDBName $smtp $From $To $rptName $rptPath $SqlQuery $connectionName $commandTimeout;
 
If you find this script useful please donate generously.

Wednesday, January 20, 2016

Converting BMP Files to JPEG Format

Here is a simple PowerShell script which does the job.


$Path = "E:\Pictures\Scanned\"
$files = Get-ChildItem $Path*.bmp
Add-Type -AssemblyName system.drawing
$imageFormat = "System.Drawing.Imaging.ImageFormat" -as [type]
ForEach ($file in $files) { 
    $outputfile =  $file.fullName -replace ".bmp", ".jpg"
    write-host $outputfile
    $image = [drawing.image]::FromFile($file.fullName)
    $image.Save($outputfile, $imageFormat::jpeg)
}
 
If you find this script useful please donate generously.

Friday, October 31, 2014

Oracle Pipelined Function

The following sample demonstrates Pipelined function in Oracle.


exec dbms_output.enable(1000000);

SET SERVEROUTPUT ON

      CREATE OR REPLACE
      TYPE table_rec FORCE AS OBJECT (
           person_code number(10)
          ,visa_type varchar2(40)
          ,prm number(10)
      );
/                  

      CREATE OR REPLACE
      TYPE my_table FORCE AS table of table_rec;
/

      CREATE OR REPLACE FUNCTION get_table_rows(
             p_person_code IN NUMBER
      ) RETURN my_table PIPELINED AS
               v_my_table my_table;
            BEGIN
                  SELECT table_rec(TBL.person_code
                                  ,TBL.visa_type
                                  ,TBL.p_person_code
                  )
                  BULK COLLECT INTO v_my_table
                  FROM
                  (SELECT person_code
                          ,visa_type
                          ,p_person_code
                  FROM visas
                  where person_code = p_person_code
                  ) TBL;

                FOR i IN v_my_table.FIRST .. v_my_table.LAST LOOP
                  PIPE ROW(v_my_table(i));
                END LOOP;
                RETURN;
            EXCEPTION  
                WHEN OTHERS THEN  
                    DBMS_OUTPUT.PUT_LINE(SQLCODE || ' ' || SQLERRM);
      END get_table_rows;
/

 The script below runs this function:

DECLARE
      CURSOR C IS select person_code
                         ,registration_no
                         ,forename
                         ,surname 
                    from table(get_table_rows(182));
   l_rec C%ROWTYPE;
   l_start date;
   l_finish date;
BEGIN
    l_start := sysdate;
     OPEN C;
     LOOP
        FETCH C INTO l_rec;
        DBMS_OUTPUT.PUT_LINE(l_rec.person_code);
        EXIT WHEN C%NOTFOUND;
     END LOOP;
     CLOSE C;
     l_finish := sysdate;
     dbms_output.put_line('Time difference = ' || (l_finish - l_start) * (24 * 60 * 60));
END;

If you find this script useful please donate generously.

Wednesday, August 28, 2013

Scan Software Installed Powershell Script

The process has been design to scan on the windows based computer  installed software and logs it to text file and SQL Server database.   It requires the tblHardware_Populated and tblSoftware_Populated tables.  The following DML commands will create them.


CREATE TABLE [dbo].[tblHardware_Populated](
 [ComputerName] [nvarchar](30) NOT NULL,
 [SerialNumber] [nvarchar](50) NULL,
 [PhysicalMemory] [nvarchar](10) NOT NULL,
 [DiskSpace] [nvarchar](max) NOT NULL,
 [Domain] [nvarchar](50) NOT NULL,
 [Manufacturer] [nvarchar](100) NULL,
 [Model] [nvarchar](100) NULL,
 [PrimaryOwnerName] [nvarchar](50) NOT NULL,
 [DateCreated] [datetime] NOT NULL,
 [JobName] [nvarchar](256) NULL,
 [ActionsPath] [nvarchar](256) NULL,
 [LastRunTime] [datetime] NULL,
 [NextRunTime] [datetime] NULL,
 [Actions] [nvarchar](256) NULL,
 CONSTRAINT [PK_tblHardware_Populated] PRIMARY KEY CLUSTERED
(
 [ComputerName] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE TABLE [dbo].[tblSoftware_Populated](
 [SoftwareID] [int] IDENTITY(1,1) NOT NULL,
 [ComputerName] [nvarchar](30) NOT NULL,
 [PackageName] [nvarchar](200) NOT NULL,
 [InstallDate] [date] NULL,
 [InstallLocation] [nvarchar](256) NULL,
 [PackageCache] [nvarchar](50) NULL,
 [Vendor] [nvarchar](200) NULL,
 [Version] [nvarchar](30) NULL,
 CONSTRAINT [PK_tblSoftware_Populated] PRIMARY KEY CLUSTERED
(
 [SoftwareID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
 ALTER TABLE [dbo].[tblSoftware_Populated]  WITH NOCHECK ADD  CONSTRAINT [FK_tblSoftware_Populated_tblHardware_Populated] FOREIGN KEY([ComputerName])

REFERENCES [dbo].[tblHardware_Populated] ([ComputerName])

GO
ALTER TABLE [dbo].[tblSoftware_Populated] NOCHECK CONSTRAINT [FK_tblSoftware_Populated_tblHardware_Populated]

GO

Application Folder Structure

The Universal Data Link file dbConnection.udl provides connection to SQL Server database and Powershell script should be located in .\InstalledSoftware\Script\ folder.  Output  data and scan.log (process log) file will be created in .\InstalledSoftware\Output\ directory.  Here is the script listing:
#This script scans installed software and log it to text file and ConfigManag database
    function datetime{
      $year = (Get-Date).Year;
      $month = Get-Date -format MM;
      $day = Get-Date -format dd;
      $hour = Get-Date -format hh;
      $minute = Get-Date -format mm;
      $second = Get-Date -format ss;
      return "$day/$month/$year $hour`:$minute`:$second";
    }
  
    function getTasks($path) {
        $out = @()
        # Get root tasks
        $schedule.GetFolder($path).GetTasks(0) | % {
            $xml = [xml]$_.xml
            $out += New-Object psobject -Property @{
                "Name" = $_.Name
                "Path" = $_.Path
                "LastRunTime" = $_.LastRunTime
                "NextRunTime" = $_.NextRunTime
                "Actions" = ($xml.Task.Actions.Exec | % { "$($_.Command) $($_.Arguments)" }) -join "`n"
            }
        }
        # Get tasks from subfolders
        $schedule.GetFolder($path).GetFolders(0) | % {
            $out += getTasks($_.Path)
        }
        #Output
        $out
    }
    $tasks = @()
    $schedule = New-Object -ComObject "Schedule.Service"
    $schedule.Connect()
    # Start inventory
    $tasks += getTasks("\")
    # Close com
    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($schedule) | Out-Null
    Remove-Variable schedule
    # Output all tasks
    #$tasks
    CLEAR-HOST
    [void][reflection.assembly]::LoadWithPartialName("microsoft.visualbasic")
    [console]::ForegroundColor = "yellow"
    $date = datetime;
    "** Start process $date **";
    write-host "`r";
    $dt = Get-Date -format "yyyy_MM_dd"
    $a = $ErrorActionPreference
    $ErrorActionPreference = "SilentlyContinue"
    try{
        $file =  "Scan_" + $dt + ".txt"
        $JobName = "Scan Software Installed"
        $folder = ".\InstalledSoftware\Output\"
        $filePath = $folder + $file
        $outlog = $folder+ "\scan.log"
        $($date) | out-file $outlog -append
        $testFileExists = Test-Path $outlog
        if ($testFileExists -eq $false) {
            "" | out-file $outlog
        }
        $scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
        $myconnectionstring = "File Name=" + $scriptPath + "\dbConnection.udl"
      
        $adOpenStatic = 3
  $adLockOptimistic = 3
        $objConnection = New-Object -comobject ADODB.Connection
        $objRecordset = New-Object -comobject ADODB.Recordset
        $objConnection.Open($myconnectionstring)
        if($objConnection.state -eq 0){
          "Could not establish connection" | out-file $outlog -append
          exit
        }
      
        $cmd = Get-ChildItem $folder -force | select name
        $testFileExists = $false
        $filesNo = 0
        foreach ($str in $cmd){
    $test = $str.name
                if ($str.name -eq $file) {
                    $testFileExists = $true
                    break
                } else {
                    $StartsWith = $test -match  "Scan"
                    if ($StartsWith -eq $true){
                        $filesNo = $filesNo + 1
                    }
                }  
        }

  #Delete existing data        if ($testFileExists -eq $false) {
            if ($filesNo -gt 0){
    Get-ChildItem $folder | Remove-Item -r -force
            }  
            New-Item $filePath -type file
        
         $strSQL = "TRUNCATE TABLE dbo.tblSoftware_Populated"
         $retval = $objConnection.Execute($strSQL)
            $strSQL = "ALTER TABLE dbo.tblSoftware_Populated DROP CONSTRAINT FK_tblSoftware_Populated_tblHardware_Populated"
            $retval = $objConnection.Execute($strSQL)
            $strSQL = "TRUNCATE TABLE dbo.tblHardware_Populated"
         $retval = $objConnection.Execute($strSQL)
            $strSQL = "ALTER TABLE dbo.tblSoftware_Populated  WITH NOCHECK ADD  CONSTRAINT FK_tblSoftware_Populated_tblHardware_Populated FOREIGN KEY(ComputerName)"
            $strSQL = $strSQL + "REFERENCES dbo.tblHardware_Populated (ComputerName)"
            $retval = $objConnection.Execute($strSQL)
            $strSQL = "ALTER TABLE dbo.tblSoftware_Populated NOCHECK CONSTRAINT FK_tblSoftware_Populated_tblHardware_Populated"
            $retval = $objConnection.Execute($strSQL)
        } 
        Add-Content $filePath $log
        $log = "*" * 50
        Add-Content $filePath $log  #Check if it is already scanned        $strSQL = "SELECT COUNT(*) counter FROM dbo.tblHardware_Populated WHERE"
        $strSQL = $strSQL + " ComputerName = '" + $($env:computername) + "'"
        $objRecordset.Open($strSQL,$objConnection,$adOpenStatic,$adLockOptimistic)
        if($objRecordset.EOF -eq $True){
     "No Data found" | out-file $outlog -append
     exit
  }
        $counter = $objRecordset.Fields.Item("counter").Value
        $objRecordset.Close()  #Populate Hardware        if ($counter -eq 0){
            $strSQL = "INSERT INTO dbo.tblHardware_Populated("
            $strSQL = $strSQL + "ComputerName"
            $strSQL = $strSQL + ",SerialNumber"
            $strSQL = $strSQL + ",PhysicalMemory"
            $strSQL = $strSQL + ",DiskSpace"
            $strSQL = $strSQL + ",Domain"
            $strSQL = $strSQL + ",Manufacturer"
            $strSQL = $strSQL + ",Model"
            $strSQL = $strSQL + ",PrimaryOwnerName"
            $strSQL = $strSQL + ",DateCreated"
            $strSQL = $strSQL + ",JobName"
            $strSQL = $strSQL + ",ActionsPath"
            $strSQL = $strSQL + ",LastRunTime"
            $strSQL = $strSQL + ",NextRunTime"
            $strSQL = $strSQL + ",Actions"
            $strSQL = $strSQL + " )VALUES("
            $strSQL = $strSQL + "'" + $($env:computername) + "'"
            $log = "Computer Name: " +$env:computername
            Add-Content $filePath $log
            $log = get-wmiobject win32_bios | select-object serialnumber
            foreach ($str in $log){
                $out = "Serial Number: " + $str.serialnumber
                $strSQL = $strSQL + ",'" + $($str.serialnumber) + "'"
                Add-Content $filePath $out
            }
          
            $txt = Get-WMIObject -class Win32_PhysicalMemory |
            Measure-Object -Property capacity -Sum |
            select @{N="~"; E={[math]::round(($_.Sum / 1GB),2)}}
            $log = $txt -replace "@{",""
            $log = $log -replace "}",""
            $log = $log -replace "=",""
            $log = $log -replace "~",""
            $strSQL = $strSQL + ",'" + $log + "'"
            $log = "Total Physical Ram: " + $log
            Add-Content $filePath $log
            $txt = ""
            $log = gwmi win32_volume -Filter 'drivetype = 3' |
            select driveletter, label, @{LABEL='GBfreespace';EXPRESSION={"{0:N2}" -f ($_.freespace/1GB)} }
            foreach ($str in $log){
                $out = "Driveletter : " + $str.driveletter + "`t"
                $txt = $txt + $out;
                #Add-Content $filePath $out
                $out = "Label : " + $str.label + "`t"
                $txt = $txt + $out;
                #Add-Content $filePath $out
                $out = "GBfreespace : " + $str.GBfreespace + "`t"
                $txt = $txt + $out;
                #Add-Content $filePath $out
            }
            $strSQL = $strSQL + ",'" + $txt + "'"
            Add-Content $filePath $txt
            $log = get-wmiobject win32_computersystem
            foreach ($str in $log){
                $out = "Domain : " + $str.Domain
                $strSQL = $strSQL + ",'" + $($str.Domain) + "'"
                Add-Content $filePath $out
                $out = "Manufacturer : " + $str.Manufacturer
                $strSQL = $strSQL + ",'" + $($str.Manufacturer) + "'"
                Add-Content $filePath $out
                $out = "Model : " + $str.Model
                $strSQL = $strSQL + ",'" + $($str.Model) + "'"
                Add-Content $filePath $out
                $out = "PrimaryOwnerName : " + $str.PrimaryOwnerName
                $strSQL = $strSQL + ",'" + $($str.PrimaryOwnerName) + "'"
                Add-Content $filePath $out
            }
            $strSQL = $strSQL + ",CONVERT(datetime,'" + $date + "',103)"
          
            $out = ""
            foreach ($str in $tasks){
                if ($str.Name -eq $JobName){
                    $out = "Name: " + $str.Name
                    $strSQL = $strSQL + ",'" + $($str.Name) + "'"
                    Add-Content $filePath $out
                    $out = "Path: " + $str.Path
                    $strSQL = $strSQL + ",'" + $($str.Path) + "'"
                    Add-Content $filePath $out
                    $out = "LastRunTime: " + $str.LastRunTime
                    $strSQL = $strSQL + ", CONVERT(datetime,'" + $($str.LastRunTime) + "',101)"
                    Add-Content $filePath $out
                    $out = "NextRunTime: " + $str.NextRunTime
                    $strSQL = $strSQL + ", CONVERT(datetime,'" + $($str.NextRunTime) + "',101)"
                    Add-Content $filePath $out
                    $out = "Actions: " + $str.Actions
                    $strSQL = $strSQL + ",'" + $($str.Actions) + "'"
                    Add-Content $filePath $out
                    Break
                }
              
            }
          
            if ($out -eq ""){
                $out = "Name: "
                $strSQL = $strSQL + ",Null"
                Add-Content $filePath $out
                $out = "Path: "
                $strSQL = $strSQL + ",Null"
                Add-Content $filePath $out
                $out = "LastRunTime: "
                $strSQL = $strSQL + ",Null"
                Add-Content $filePath $out
                $out = "NextRunTime: "
                $strSQL = $strSQL + ",Null"
                Add-Content $filePath $out
                $out = "Actions: "
                $strSQL = $strSQL + ",Null"
                Add-Content $filePath $out
            }
          
            $strSQL = $strSQL + ")"
            $retval = $objConnection.Execute($strSQL)
            $log = "*" * 50
            Add-Content $filePath $log
            Add-Content $filePath ""
            $log = " " * 15 + "Software Installed"
            Add-Content $filePath $log
        
   #Populate Software            Add-Content $filePath ""
            $log = Get-WmiObject -Class Win32_Product -ComputerName . |
             select Name,InstallDate,InstallLocation,PackageCache,Vendor,Version,IdentifyingNumber
            foreach ($str in $log){
                $strSQL = "INSERT INTO dbo.tblSoftware_Populated("
                $strSQL = $strSQL + "ComputerName"
                $strSQL = $strSQL + ",PackageName"
                $strSQL = $strSQL + ",InstallDate"
                $strSQL = $strSQL + ",InstallLocation"
                $strSQL = $strSQL + ",PackageCache"
                $strSQL = $strSQL + ",Vendor"
                $strSQL = $strSQL + ",Version"
                $strSQL = $strSQL + " )VALUES("
                $strSQL = $strSQL + "'" + $($env:computername) + "'"
                $out = "Name: " + $str.Name
                $strSQL = $strSQL + ",'" + $($str.Name) + "'"
                Add-Content $filePath $out
                $dt = $str.InstallDate
                $dtd = ",null"
                if ($dt) {
                    $dta = $dt.Substring(6,2) + "/" + $dt.Substring(4,2) + "/" + $dt.Substring(0,4)
                    $dtd = ",'" + $dt + "'"
                }
                $out = "InstallDate: " + $dta
                $strSQL = $strSQL + $dtd
                Add-Content $filePath $out
                $loc = ",null"
                if ($str.InstallLocation) {
                    $loc = ",'" + $str.InstallLocation + "'"
                }  
                $out = "InstallLocation: " + $str.InstallLocation
                $strSQL = $strSQL + $loc
                Add-Content $filePath $out
                $out = "PackageCache: " + $str.PackageCache
                $strSQL = $strSQL + ",'" + $($str.PackageCache) + "'"
                Add-Content $filePath $out
                $out = "Vendor: " + $str.Vendor
                $strSQL = $strSQL + ",'" + $($str.Vendor) + "'"
                Add-Content $filePath $out
                $out = "Version: " + $str.Version
                $strSQL = $strSQL + ",'" + $($str.Version) + "'"
                Add-Content $filePath $out
                $strSQL = $strSQL + ")"
                Add-Content $filePath ""
                #Write-Output $strSQL
                $retval = $objConnection.Execute($strSQL)
            }
            $JobStatus = "OK"
        }  
    } catch [Exception] {
            "$($_.InvocationInfo.ScriptName)($($_.InvocationInfo.ScriptLineNumber)): $($_.InvocationInfo.Line)" | out-file $outlog -append
         $("Error catched: " + $_.Exception.GetType().FullName) | out-file $outlog -append
         $("Error catched: " + $_.Exception.Message) | out-file $outlog -append
         $JobStatus = "not OK"
         continue;
    }
    $ErrorActionPreference = $a
    $objConnection.Close()
    $date = datetime;
    "** End of process $date **";
 
The process can be scheduled according your requirements.

If you find this script useful please donate generously.

Monday, February 4, 2013

Exporting pdf documents properties to the text file

The following script extracts pdf documents some property fields and dump them to the text file.  Note that if itextsharp.dll does not exist on your system it can be downloaded from http://sourceforge.net/projects/itextsharp/.  To add other fields to output see documentation on http://www.adobe.com.

[System.Reflection.Assembly]::LoadFrom(\\your_server\..\itextsharp.dll)

function Get_PDF_Properties($path){
    Try{
        $raf = New-object iTextSharp.text.pdf.RandomAccessFileOrArray($path)
        $reader = New-object iTextSharp.text.pdf.PdfReader($raf, $Nothing)
        $myPDF= ""| select Title,Subject
        $mypdf.title=($reader).info.item("Title")
        $sub = 0
        if ((($reader).info.item("Subject").Length -ne 0)){
            $mypdf.subject=($reader).info.item("Subject")
            $sub = 1
        } 
    }
    Catch [Exception]{
        if ($sub -eq 0) {
            $mypdf.subject=""
        } else {
            Write-Host "Generic Exception"
            Write-Host $_
            $_ | Select *
        } 
    }
  return $mypdf
}
$in = \\your_server\..\
$out = "\\your_server\..\file_name.txt"
Add-Content -Path $out -Value "ImagePath`tTitle`tSubject" -Encoding UTF8
$dirstr = Get-ChildItem $in -recurse -filter "*.pdf" | Select-Object FullName
foreach ($objItem in $dirstr) {
    $path = $objItem -replace "@{FullName=", ""
    $path = $path -replace "}", ""
    $mypdf = Get_PDF_Properties($path)
    $pdf = $mypdf -replace "@{Title=", ""
    $pdf = $pdf -replace " Subject=", ""
    $pdf = $pdf -replace "}", ""
    $pdf = $pdf -replace ";", "`t"
    write-host "$path`t$pdf"
    Add-Content -Path $out -Value "$path`t$pdf" -Encoding UTF8
}
 
If you find this script useful please donate generously.

Thursday, December 13, 2012

Powershell script to Refresh Excel Data

The following sript opends Excel files one by one, refreshes the data, saves the file back and closes the file.


 Function RefreshExcelData($Excel){
  $App = new-object -comobject excel.application
  $App.Workbooks.Open($Excel)
  $App.Visible = $true
  $App.ActiveWorkbook.Refreshall()
  $App.Save()
  $App.Quit()
  $username = $env:username
  Remove-Item "C:\Users\$username\Documents\RESUME.XLW"
 }

 CLEAR-HOST
 $Host.UI.RawUI.ForegroundColor = "yellow"
 $Excel = 'R:\folder\file1.xlsx'
 RefreshExcelData $Excel
 $Excel = 'R:\folder\file2.xlsx'
 RefreshExcelData $Excel
 $Excel = 'R:\folder\file3.xlsx'
 RefreshExcelData $Excel
 $Host.UI.RawUI.ForegroundColor = "white"

If you find this script useful please donate generously.