Azure is fun. FUN, I say!
Ports:
1433 - standard SQL port
11000-11999 - ports if you're using Proxy instead of Redirect
14000-14999 (!) - ports for the DAC (Dedicated Admin Connection)
Put butter in your mixer, add sugar, set on low. Write a stored procedure to keep track of jobs that won't run. Add flour. Use a DMV to find unused indexes. Add eggs. Bake in an MDF with 20% free space.
Azure is fun. FUN, I say!
Ports:
1433 - standard SQL port
11000-11999 - ports if you're using Proxy instead of Redirect
14000-14999 (!) - ports for the DAC (Dedicated Admin Connection)
Just found out about this the past month.
I like diagrams for my documentation, and I detest making it. I also would like to build it via script, since that's more useful.
Sample:
graph TD;
A-->B;
A-->C;
B-->D;
C-->D;
Which produces:
Pretty cool right?
VSCode supports it (load the extension "Markdown Preview Enhanced"), but Jira and Confluence don't support it natively....
But there's a workaround!
In Chrome, load the extension Mermaid Previewer (https://chrome.google.com/webstore/detail/mermaid-previewer/oidjnlhbegipkcklbdfnbkikplpghfdl)
There are a couple of tweaks. For one, you need to enable sandbox mode in the extension.
For Confluence, I put the site as my\.confluence\.site and a selector of pre > code. To add it, I create a markdown block, but within it do this:
```mermaid
graph LR
A --> B
```
(update 2023/10/27) another way that seems to work for confluence is to make a regular code block ( for me, type ``` by hand, then do it with just the "graph LR / A --> B"), and in Mermaid Previewer as div.container, but I don't know how that affects other "containers". the nice thing about doing it the other way is that (for some reason) you HAVE to have the ```mermaid, which means it can't accidentally go crazy with it.
For Jira, my selector is pre.code-java, since by default our code blocks are Java. You can look at the element in Chrome developer mode (right-click, choose "inspect").
Then, within the Jira ticket, just do:
graph LR
A --> B
(NOTE THE SPACES!) No idea why it doesn't act the same between them, but for me it's fine.
SSIS has a LOT of ways to set configs. Parameters & variables, and then those can be overridden at different levels.
in order (from lowest to highest) - bottom of this list supercedes the top. You can see WHERE it was overridden because at that level, the parameter name is in bold.
This post is in response to an incredibly frustrating interaction with Azure.
I figured I would do something incredibly simple - import a parquet file from blob storage into Azure SQL DB. When the file is written to Blob Storage (using CETAS, fwiw), it should activate the storage trigger, which should kick off the pipeline.
The problem is that it won't work as the tutorial (https://learn.microsoft.com/en-us/azure/data-factory/tutorial-copy-data-portal) shows. It says it can't find the file. Why not? Well, it wasn't firewalls or anything else. It's that a linked service dataset, in "Sink" requires a container name - and the trigger sends the container name also. So I kept getting messages that it couldn't find \mycontainer\mycontainer\myfolder\myfile.parquet . I found a single message on Stack Overflow (https://stackoverflow.com/questions/67294521/azure-data-factory-how-to-get-new-file-location-from-triggerbody-folderpath/74738786) on how to solve it. So, here we go:
package parameters:
Source:
Never, for the love of god, run update-module -all.
I went from 1.23 to 1.27 and my scripts broke.
First problem: incompatibility with Microsoft.Graph.Authentication. I had multiple copies of it, and uninstall older versions didn't work - had to uninstall ALL of microsoft.graph (not easy, whhhhyyyy), then reinstall WITH "-requiredversion 1.27" (or was it 1.27.00?). Otherwise it still grabbed the wrong versions somehow.
Second problem: invalid filter clause
get-mgchat : Invalid filter clause
At line:1 char:1
+ get-mgchat -all -PageSize 50 -filter "lastUpdatedDateTime gt '2023-05...
why? Because between 1.23 and 1.27, they stopped accepting the datestring being in quotes. Now, if it is, it doesn't work. What the everliving...
Basic script to update all your Visual Studio instances. On some servers I have/need 3.
The VSSetup is the magic, courtesy of Microsoft (and hence the prereqs), the rest is just running their installer.
#this didn't find the 2022 install. Run as ISE admin
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -force
set-psrepository psgallery -InstallationPolicy Trusted
Install-Module VSSetup
get-vssetupinstance|%{
$installpath = $_.installationpath
"$installpath"
Start-Process -Wait -FilePath "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vs_installer.exe" -ArgumentList "update --passive --norestart --installpath ""$installpath"""
}
#this should work, but doesn't. Just kicks back instantly.
#Start-Process -Wait -FilePath "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vs_installer.exe" -ArgumentList "updateall --passive --norestart"
I need this because IE tries to load about:config twice, which throws errors in the secure mode of IE/Edge, and I needed to script a way around it.
Thanks to Joe for the tip on quotes.
if (-not (Test-Path -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\EscDomains\blank'))
{
$null = New-Item -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\EscDomains\blank'
}
Set-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\EscDomains\blank' -Name "about" -Value 2 -Type DWord
And we're back! Not that anybody noticed. 'Scool. IP agreements and the like.
I'll make it brief since I logged on at 11pm to get this working.
TL;DR - the below lines will allow you to query a table on your MI, creating Parquet files in Azure blob storage. And you can query it! Next up is partitioning over time, etc, etc. But this is freaking fantastic. I have a python script I wrote that does it, but it's nowhere as nice/easy as this.
Why do you care? Because it's a fantastically easy way to archive older data to blob storage, and I suspect (need to test) that if you do it right, you can then have it go to cool/archive storage via a lifecycle setup, so that if you need it much later, you can. Parquet is a columnar compressed format that can then feed other things like Snowflake, Hadoop, Redshift Spectrum, AWS Athena, etc. For me it's a platform-agnostic, portable, highly-compressed data format that I can use to better archive older data.
CETAS: Create External Table As Select
1) Walking the dog and reading my RSS feeds on Feedly, I see this:
https://azure.microsoft.com/en-us/updates/azure-sql-general-availability-updates-for-lateapril-2023/
3) Run back home and log in. My poor dog was out of breath.
4) After dinking for 30 minutes trying to get my local copy of the az powershell module to work with the command, (still isn't working, seriously, wtaf), and being unable to use cloud shell since it requires cloud storage and permissions are locked down too much, I finally use -UseDeviceauthentication and run these commands on my instance:
# Enable ServerConfigurationOption with name "allowPolybaseExport"
Set-AzSqlServerConfigurationOption -ResourceGroupName "myresource_group_name" -InstanceName "myManagedInstanceName" `
-Name "allowPolybaseExport" -Value 1
5) Verify it works.
SELECT [name], [value] FROM sys.configurations WHERE name ='allow polybase export';
Returns 1!
6) now, I use the following t-sql, pretty much verbatim from their example and a SAS token. The only magic I've added is the 10 minutes on how to use a SAS token.
USE my
GO
CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'password would go here';
GO
CREATE DATABASE SCOPED CREDENTIAL [CETASCredential]
WITH IDENTITY ='SHARED ACCESS SIGNATURE',
SECRET = 'sp=rwl&st=2023-04-27T04:37:23Z&se=2023-05-26T12:37:23Z&spr=https&sv=2021-12-02&sr=c&sig=stringofstuffremovedhahahahahthisisfromthesastokenA%3D' ; --Removing leading '?'
GO
CREATE EXTERNAL DATA SOURCE [CETASExternalDataSource]
WITH (
LOCATION = 'abs://mycontainer@mystorageacct.blob.core.windows.net',
CREDENTIAL = [CETASCredential] );
GO
CREATE EXTERNAL FILE FORMAT [CETASFileFormat]
WITH(
FORMAT_TYPE=PARQUET,
DATA_COMPRESSION = 'org.apache.hadoop.io.compress.SnappyCodec'
);
GO
-- Count how many rows we plan to offload
SELECT COUNT(*) FROM my.dbo.table
CREATE EXTERNAL TABLE mytable_polybase_example
WITH (
LOCATION = 'I/get/multiple/paths/for/multiple/tables!/',
DATA_SOURCE = [CETASExternalDataSource],
FILE_FORMAT = [CETASFileFormat])
AS
SELECT
*
FROM
my.dbo.table
-- you can query the newly created external table
SELECT COUNT (*) FROM mytable_polybase_example;
I wish I knew whom to thank for this. An absolute genius idea. Probably Ozar, possibly Bertrand, could be White or a bunch of other #SQLFamily. Warning: once you use this, you'll want it on all your servers.
Premise: run an Extended Event looking for all errors over Severity 10, saving in a 10mb memory buffer within the SQL Server instance. Keep the rolling last errors so you have a decent amount of history. I believe the original was Severity 10 and above, but that one was too chatty for me, whereas 11+ seems to be ideal. But I refer to it as my "Sev10" code, so here we are. This includes all sorts of stuff you won't normally see - errors from stored procedures, errors in replication, errors in SSMS, etc.
Testing it is easy: create session & start it, run "select 1/0", then run the query.
I do have a version that I run that saves logs out, but honestly, that's a different post. This Xevent uses 10mb from the Ring Buffer, constantly filling and keeping the last 10mb of errors. Performance hit seems minimal, and we're doing millions of transactions a day.
Performance has always been the kicker on this - using Xquery directly against the ring buffer is slow as heck, and even after solving that via temp tables, I still ran into problems with one particular server. That made me realize I don't appear to have posted this. By splitting it via NODE to multiple rows to a temp table, then querying the temp table to shred the XML, the performance is vastly improved. In my case, from 8 minutes to 16 seconds. And on most servers, under 5.
Other thoughts - you might consider adding another WHERE clause to the ADD EVENT; maybe filter end-users' machines or SSMS. I like having it, but you do you. Turns out SSMS is damn chatty sometimes, also. "View server state permission was denied " and "Cannot drop the table '#SVer'" abound.
Want to run this in Azure SQL DB? Sure, just do it against each database, replacing the "ON SERVER" with "ON DATABASE". The table names you query against change slightly (it's commented), and the time zone calculation doesn't work yet. Booooooooo.
I'm working on a collector, just haven't finished yet.
Bonus! Frames! Take the Frames provided in the Event_Data, and go use their amazing code to tell you exactly what piece of code in what function/stored procedure, and what line, caused the issue. Is it in a stored procedure calling a stored procedure calling a function? This will tell you where and what. Genius, and kudos to Tom. https://straightforwardsql.com/posts/investigating-errors-with-extended-events/
Cheers!
This is a barebones Xevent (Extended Event) . I'm not using blob storage, just the existing "eh, you have a couple hundred meg worth of ring buffer you can use". But I needed to see who was using one of our dev instances. I thought we'd moved everything over to the Azure SQL DBs, since there's a ton of reasons to do so.
Here's some quick off-the-cuff reasons why I prefer Azure SQL DB to Managed Instance:
Yes, there are downsides. CLR, Replication, Agent, Mail, others. But the wins of using Azure SQL DB are very tangible. < / soapbox >
Anyhow, script:
@@
/*
CREATE EVENT SESSION [Logins] ON SERVER
ADD EVENT sqlserver.login(
ACTION(sqlserver.client_hostname,sqlserver.client_app_name,sqlserver.database_name,sqlserver.username))
ADD TARGET package0.ring_buffer(SET max_memory = 4096) --max_event_limit threw error!
go
ALTER EVENT SESSION [Logins]
ON SERVER
STATE = start ;
GO*/
if object_id('tempdb..#xevent') is not null
DROP TABLE #xevent
CREATE TABLE #xevent (target_data XML)
INSERT INTO #xevent ( target_data)
SELECT target_data
FROM sys.dm_xe_session_targets AS st
INNER JOIN sys.dm_xe_sessions AS se
ON CAST(se.address AS BINARY(8)) = CAST(st.event_session_address AS BINARY(8))
WHERE
se.name = 'Logins'
DECLARE @count VARCHAR(10)
SELECT @count = target_data.value('/RingBufferTarget[1]/@eventCount','varchar(10)')
FROM #xevent --null means it's not on or not "installed"
PRINT 'eventCount = ' + @count
;with events_cte as(
SELECT
ed.c.value('(@timestamp)[1]', 'DATETIMEOFFSET') at TIME ZONE 'Central Standard Time' AS err_timestamp,
--xevents.event_data.value('(RingBufferTarget/event/@timestamp)[1]', 'datetime2')) AS [err_timestamp],
ed.c.value('(action[@name="client_app_name"]/value)[1]', 'nvarchar(max)') AS client_app_name,
ed.c.value('(action[@name="client_hostname"]/value)[1]', 'nvarchar(max)') AS client_hostname,
ed.c.value('(action[@name="database_id"]/value)[1]', 'int') AS database_id,
ed.c.value('(action[@name="database_name (Action)"]/value)[1]', 'nvarchar(max)') AS nt_username,
ed.c.value('(action[@name="username"]/value)[1]', 'nvarchar(max)') AS username
, ed.c.query('.') AS event_data --we leave this off since it makes this run SO MUCH LONGER.
FROM #xevent a
cross apply (select CAST(target_data as XML) as event_data) AS xevents
CROSS APPLY event_data.nodes('/RingBufferTarget/event') AS ed(c)
)
SELECT *
--, events_cte.event_data
from events_cte
order by err_timestamp desc;
@@