Back to Blog
5 min read

SSRS to Power BI Migration: A Practical Guide

SQL Server Reporting Services (SSRS) has been a enterprise reporting staple for decades. As organizations modernize, Power BI offers compelling advantages. Let’s explore migration strategies and tools.

Why Migrate to Power BI?

  • Self-service analytics: Users can create their own reports
  • Modern visualizations: Interactive, mobile-friendly reports
  • Cloud-first: Scales without infrastructure management
  • AI capabilities: Natural language Q&A, smart narratives
  • Ecosystem integration: Teams, SharePoint, Excel

Migration Options

Option 1: Power BI Report Server

On-premises Power BI with SSRS-like deployment:

# Install Power BI Report Server
$installerPath = "PowerBIReportServer.exe"
Start-Process -FilePath $installerPath -ArgumentList '/quiet /IAcceptLicenseTerms' -Wait

# Configure
$config = @{
    DatabaseServer = "sql-server"
    DatabaseName = "ReportServer"
    VirtualDirectory = "Reports"
    ReportServerUrl = "https://reports.contoso.com"
}

Set-PbiReportServerConfiguration @config

Option 2: Power BI Service (Cloud)

Full cloud migration:

# Connect to Power BI Service
Connect-PowerBIServiceAccount

# Create workspace
$workspace = New-PowerBIWorkspace -Name "Migrated Reports"

# Publish report
Publish-PowerBIReport -Path ".\ConvertedReport.pbix" -Workspace $workspace

# Configure data refresh
$report = Get-PowerBIReport -WorkspaceId $workspace.Id -Name "ConvertedReport"
$dataset = Get-PowerBIDataset -WorkspaceId $workspace.Id -Id $report.DatasetId

# Set up gateway for on-premises data
$gateway = Get-DataGatewayCluster | Where-Object { $_.Name -eq "On-Premises Gateway" }
Set-PowerBIDatasetDatasource -DatasetId $dataset.Id -GatewayId $gateway.Id

Conversion Approaches

Using RDL Migration Tool

Microsoft provides a tool for basic conversions:

# Install RDL Migration Tool
Install-Module -Name RdlMigration -Force

# Analyze report complexity
$analysis = Test-RdlMigration -Path ".\Report.rdl"
$analysis | Format-List

# Convert report
Convert-RdlToTbix -Path ".\Report.rdl" -OutputPath ".\ConvertedReport.pbix"

Manual Conversion Steps

For complex reports, manual conversion is often required:

# Script to analyze SSRS reports and generate conversion checklist
import xml.etree.ElementTree as ET
from dataclasses import dataclass
from typing import List

@dataclass
class ReportElement:
    element_type: str
    name: str
    complexity: str
    pbi_equivalent: str
    notes: str

def analyze_rdl(rdl_path: str) -> List[ReportElement]:
    """Analyze RDL file and generate conversion checklist."""

    tree = ET.parse(rdl_path)
    root = tree.getroot()
    ns = {'rd': 'http://schemas.microsoft.com/sqlserver/reporting/2016/01/reportdefinition'}

    elements = []

    # Analyze data sources
    for ds in root.findall('.//rd:DataSource', ns):
        elements.append(ReportElement(
            element_type='DataSource',
            name=ds.get('Name'),
            complexity='Low',
            pbi_equivalent='Data Source in Power Query',
            notes='Recreate connection in Power BI'
        ))

    # Analyze datasets
    for dataset in root.findall('.//rd:DataSet', ns):
        query = dataset.find('.//rd:CommandText', ns)
        complexity = 'High' if query and len(query.text) > 500 else 'Low'
        elements.append(ReportElement(
            element_type='DataSet',
            name=dataset.get('Name'),
            complexity=complexity,
            pbi_equivalent='Power Query or DirectQuery',
            notes='Convert to M or DAX'
        ))

    # Analyze visualizations
    viz_mapping = {
        'Tablix': ('Table/Matrix', 'Low'),
        'Chart': ('Power BI Chart', 'Medium'),
        'Gauge': ('KPI/Gauge Visual', 'Low'),
        'Map': ('Map Visual', 'High'),
        'Image': ('Image', 'Low'),
        'Subreport': ('Drillthrough/Bookmark', 'High')
    }

    for viz_type, (pbi_eq, complexity) in viz_mapping.items():
        for viz in root.findall(f'.//rd:{viz_type}', ns):
            elements.append(ReportElement(
                element_type=viz_type,
                name=viz.get('Name'),
                complexity=complexity,
                pbi_equivalent=pbi_eq,
                notes=''
            ))

    # Check for expressions
    for expr in root.findall('.//*[rd:Value]', ns):
        value = expr.find('rd:Value', ns)
        if value is not None and value.text and value.text.startswith('='):
            elements.append(ReportElement(
                element_type='Expression',
                name=expr.get('Name', 'Unknown'),
                complexity='Medium',
                pbi_equivalent='DAX Measure',
                notes=f'Convert: {value.text[:50]}...'
            ))

    return elements

# Generate report
elements = analyze_rdl('SalesReport.rdl')
for elem in elements:
    print(f"{elem.element_type}: {elem.name} ({elem.complexity})")

Converting Common SSRS Features

Expressions to DAX

// SSRS Expression
=Sum(Fields!Sales.Value)

// Power BI DAX
Total Sales = SUM('Sales'[Sales])
// SSRS Conditional formatting
=IIF(Fields!Sales.Value > 10000, "Green", "Red")

// Power BI - Create measure + conditional formatting rule
Sales Status =
IF(SUM('Sales'[Sales]) > 10000, "High", "Low")

Parameters to Slicers

SSRS parameters become Power BI slicers or filters:

{
  "visualType": "slicer",
  "dataRoles": {
    "Values": {
      "items": [
        { "queryRef": "Sales.Region" }
      ]
    }
  },
  "objects": {
    "selection": {
      "singleSelect": false,
      "selectAll": true
    },
    "header": {
      "show": true,
      "fontColor": "#000000"
    }
  }
}

Subreports to Drillthrough

// Main report measure with drillthrough
Sales Amount =
VAR CurrentSales = SUM('Sales'[Amount])
RETURN CurrentSales

// Drillthrough page shows detail
// Configure in Power BI Desktop:
// 1. Create detail page
// 2. Add drillthrough filter
// 3. Add back button

Handling Paginated Reports

For pixel-perfect, printable reports, use Power BI Paginated Reports:

<!-- RDL format supported in Power BI Premium -->
<Report xmlns="http://schemas.microsoft.com/sqlserver/reporting/2016/01/reportdefinition">
  <DataSources>
    <DataSource Name="AdventureWorks">
      <ConnectionProperties>
        <DataProvider>SQL</DataProvider>
        <ConnectString>Data Source=server;Initial Catalog=AdventureWorks</ConnectString>
      </ConnectionProperties>
    </DataSource>
  </DataSources>
  <DataSets>
    <DataSet Name="SalesData">
      <Query>
        <DataSourceName>AdventureWorks</DataSourceName>
        <CommandText>
          SELECT * FROM Sales.vSalesOrders
          WHERE OrderDate BETWEEN @StartDate AND @EndDate
        </CommandText>
      </Query>
    </DataSet>
  </DataSets>
  <!-- Rest of report definition -->
</Report>

Upload to Power BI Service:

# Upload paginated report to Power BI Premium workspace
$workspace = Get-PowerBIWorkspace -Name "Finance Reports"

# Requires Premium capacity
$report = New-PowerBIReport `
    -Path ".\InvoiceReport.rdl" `
    -Name "Invoice Report" `
    -WorkspaceId $workspace.Id `
    -ConflictAction CreateOrOverwrite

Data Gateway Configuration

For reports accessing on-premises data:

# Install gateway
$gatewayInstaller = "GatewayInstall.exe"
Start-Process -FilePath $gatewayInstaller -ArgumentList '/quiet' -Wait

# Configure gateway
$gateway = Get-DataGatewayCluster

# Add data source
Add-DataGatewayClusterDatasource `
    -GatewayClusterId $gateway.Id `
    -DatasourceType "SQL" `
    -ConnectionDetails @{
        server = "sql-server.contoso.com"
        database = "AdventureWorks"
    } `
    -CredentialType "Windows" `
    -Credential (Get-Credential)

# Map to datasets
$datasets = Get-PowerBIDataset -WorkspaceId $workspace.Id
foreach ($dataset in $datasets) {
    Set-PowerBIDatasetDatasource `
        -DatasetId $dataset.Id `
        -GatewayId $gateway.Id `
        -DatasourceId $datasource.Id
}

Migration Checklist

pre_migration:
  - Inventory all SSRS reports
  - Document data sources and schedules
  - Identify report consumers
  - Assess licensing requirements (Premium for paginated)
  - Plan gateway infrastructure

conversion:
  - Prioritize by business value
  - Start with simple reports
  - Test with sample data
  - Validate calculations
  - Review with users

deployment:
  - Set up workspaces
  - Configure security (row-level security)
  - Establish refresh schedules
  - Create deployment pipeline (Dev/Test/Prod)
  - Document new report locations

post_migration:
  - Train users
  - Monitor usage
  - Decommission old SSRS reports
  - Optimize performance
  - Gather feedback

Conclusion

SSRS to Power BI migration requires careful planning but offers significant benefits. Use Power BI Report Server for a gradual transition, or go fully cloud with Power BI Service. For complex paginated reports, Power BI Premium’s paginated report capability provides a compatible path. Start with high-value, simple reports and build expertise before tackling complex migrations.

Resources

Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.