FLOOR.S
, CIEL.S
, ROUND.S
, FLOAT.S
, and UFLOAT.S
in a single inline-assembly instruction to convert between fixed-point and float values.There are alternate double versions of the instructions in the xtensa ISA but they are not implemented in the ESP32 tensilica cores.
You can find the full xtensa assembly instruction set documentation on the Cadence website https://www.cadence.com/content/dam/cadence-www/global/en_US/documents/tools/ip/tensilica-ip/isa-summary.pdf.
If you’re writing code that uses floating point numbers you’re already using some of these instructions. The toolchain will use them whenever you convert between int
/uint
and float
types.
The instructions have a third parameter which specifies how many bits come after the decimal point (fractional bits). A value of 1
would be ±0.5 in the last bit, 2
would be ±0.25 in the last two bits, etc.
The normal assembly emitted when doing something like float f = (int)53
will have it set to 0 (no fractional bits), but by calling the instruction manually you can specify the precision.
The fractional bits parameter is a 0..15 constant and cannot be specified at runtime, so if you need different shapes you would just implement as separate methods, templates, or overloading methods with custom types.
For example, the instruction FLOAT.S f0, a2, 0
with 53
in register a2
would convert 53
to 53.0f
in the f0
register. However the instruction FLOAT.S f0, a2, 1
would convert it to 26.5f
.
Fractional Bits | Example Instruction | Input Binary | Input Int | Result Float |
---|---|---|---|---|
0 (normal float<>int) | FLOAT.S f*n*, a*n*, 0 |
0b00110100 | 52 | 52.0 |
0 (normal float<>int) | FLOAT.S f*n*, a*n*, 0 |
0b00110101 | 53 | 53.0 |
1 (lsb is ±0.5) | FLOAT.S f*n*, a*n*, 1 |
0b00110100 | 52 | 26.0 |
1 (lsb is ±0.5) | FLOAT.S f*n*, a*n*, 1 |
0b00110101 | 53 | 26.5 |
2 (lsb is ±0.25) | FLOAT.S f*n*, a*n*, 2 |
0b00110100 | 52 | 13.0 |
2 (lsb is ±0.25) | FLOAT.S f*n*, a*n*, 2 |
0b00110101 | 53 | 13.25 |
2 (lsb is ±0.25) | FLOAT.S f*n*, a*n*, 2 |
0b00110110 | 54 | 13.50 |
The FLOOR.S
, CIEL.S
, and ROUND.S
instructions convert float to fixed-point by the method in their name. Since fixed-point can’t try to store arbitrary real numbers you are telling the processor how you’d like the number to be butchered. The FLOAT.S
and UFLOAT.S
instructions just use the default rounding/inf/NaN settings to convert fixed-point to float.
Shameless plug: I’m going to be using ImHex in this post and. It has an eye-watering featureset and a bunch of existing patterns WerWolv/ImHex-Patterns. Definitely recommend that you check it out for binary data analysis.
You can actually edit the recovery actions of a service to include more steps that the first, second, and subsequent options available in the UI. Before I explain I just want to get the following points out of the way:
With that out of the way, service recovery actions as described in the services.msc
settings are stored as a binary blob in the registry. You’ll find the blob at HKLM:\\SYSTEM\CurrentControlSet\Services\[SERVICENAME]
under the FailureActions
binary key. I’m going to generate an example blob that does the following so that we can review it in more detail.
Fortunately for us this isn’t as opaque as it seems. It’s a pretty simple instance of SERVICE_FAILURE_ACTIONS with an array of SC_ACTION. We can use the following ImHex pattern code to parse this for us:
Now we pop that blob and the pattern into ImHex and we get a nice, intuitive breakdown of the binary:
With that you should have an idea where we’re going with this. You can change the lpsaActions array to as many as 1024 entries which I find hilarious. All you need to do is change the cActions value and add your new actions. We’re going to change it to 4 entries.
Var | Offset | Hex | Value | ||
---|---|---|---|---|---|
dwResetPeriod | 0x00 : 0x03 | 0x00000000 | 0 Seconds | ||
lpRebootMsg | 0x04 : 0x07 | 0x00000000 | N/A (no pointer to a reboot message string) | ||
lpCommand | 0x08 : 0x0B | 0x00000001 | cmd.exe (value of 1 means load command from other registry keys) | ||
cActions | 0x0C : 0x0F | 0x00000004 | Array size of 4 Actions | ||
*lpsaActions | 0x10 : 0x13 | 0x00000014 | Pointer to the array (next DWORD) | ||
lpsaAction[0]:Type | 0x14 : 0x17 | 0x00000001 | SC_ACTION_TYPE RESTART (restart service) | ||
lpsaAction[0]:Delay | 0x18 : 0x1B | 0x0000EA60 | Delay 60000 ms | ||
lpsaAction[1]:Type | 0x1C : 0x1F | 0x00000001 | SC_ACTION_TYPE RESTART (restart service) | ||
lpsaAction[1]:Delay | 0x20 : 0x23 | 0x0000EA60 | Delay 60000 ms | ||
lpsaAction[2]:Type | 0x24 : 0x27 | 0x00000001 | SC_ACTION_TYPE RESTART (restart service) | ||
lpsaAction[2]:Delay | 0x28 : 0x2B | 0x0000EA60 | Delay 60000 ms | ||
lpsaAction[3]:Type | 0x2C : 0x2F | 0x00000003 | SC_ACTION_TYPE RUN_CMD (run a command) | ||
lpsaAction[3]:Delay | 0x30 : 0x33 | 0x0000EA60 | Delay N/A |
I was setting up paperless-ngx as an experiment for personal document management and wanted to be able to ingest emails & attachments from an O365 account. I wanted to be able to be able to tell Paperless to ingest an email by setting a category flag (e.g. Paperless-Inbox) so that I could archive emails without moving them from their folders. Unfortunately Exchange On-Prem and Online do not support the KEYWORDS search (See Exchange IMAP Standards V0039).
Note: The email library used by paperless-ngx does not support Modern Authentication, so I built a container to host the headless version of email-oauth2-proxy
While looking for workarounds I found that Exchange does provide the email categories in a Keywords:
header; however, despite Exchange including Keywords: My-Category
in the FETCH n (BODY[HEADERS])
response, a search using SEARCH HEADER "Keywords" ""
returns no results. Sad trombone.
So if you want to find your categories in an IMAP search your categories must not have spaces and you must use a TEXT search.
SEARCH BODY "first"
works, SEARCH BODY "second"
works, but SEARCH BODY "first second"
gives no results).TEXT
search of a message! This still has the same no-spaces and all-lowercase limitations as a regular BODY search.IMAP Command | Finds Category Flags? | Find Body Text? |
---|---|---|
BODY (Quoted String) | No | Yes (lowercase only, no spaces) |
BODY (String Literal) | No | Yes (lowercase only, no spaces) |
TEXT (Quoted String) | Yes | Yes (lowercase only, no spaces) |
TEXT (String Literal) | Yes | Yes (lowercase only, no spaces) |
HEADERS | No | No (duh) |
I was experimenting with some data recovery stuff and needed some classic fixed VHD’s. I started creating a 1 TB VHD with New-VHD
but needed to change it so I hit Ctrl+C and assumed that would cancel the cmdlet and the VHD creation. I assumed incorrectly.
When I tried to delete the VHD I got this error:
I figured there was still a job creating the VHD so I scanned Virtualization CIM Classes and found the CIM_ConcreteJob class, after a quick poke this seemed to be what I was looking for.
Get-CimInstance -Namespace root\virtualization\v2 -ClassName CIM_ConcreteJob | select InstanceID, Caption, Description, Name, Status, StatusDescriptions, JobStatus, PercentComplete, Cancellable
InstanceID : "D65CC6A8-E7B4-45E4-A13D-CF7BAD642912"
Caption : Virtual Hard Disk Creation
Description : Creating Virtual Hard Disk
Name : Virtual Hard Disk Creation
Status : OK
StatusDescriptions : {Job is running}
JobStatus : Job is running
PercentComplete : 7
Cancellable : True
Now, about killing it: CIM_ConcreteJob.RequestStateChange(RequestedState, TimeoutPeriod). Looks simple enough!
Alright, lets try to request a safe/clean termination with a 30 second timeout.
$job = Get-CimInstance -Namespace root\virtualization\v2 -ClassName CIM_ConcreteJob -Filter 'InstanceID = "D65CC6A8-E7B4-45E4-A13D-CF7BAD642912"'
$job | Invoke-CimMethod -MethodName RequestStateChange -Arguments @{RequestedState = 4;TimeoutPeriod = (Get-Date).AddSeconds(30)}
ReturnValue PSComputerName
----------- --------------
32773
Annnd the return code isn’t in the list. On the upside that looks like a common return code (we’ll come back to that in a minute). Let’s try the Kill command:
$job | Invoke-CimMethod -MethodName RequestStateChange -Arguments @{RequestedState = 5;TimeoutPeriod = (Get-Date).AddSeconds(30)}
----------- --------------
32773
Great, same error. The doc says you can supply a null to indicate there’s no timeout requirement so we’ll try that instead:
$job | Invoke-CimMethod -MethodName RequestStateChange -Arguments @{RequestedState = 5;TimeoutPeriod = $null}
ReturnValue PSComputerName
----------- --------------
32775
Sigh, At least it’s different error. Alright, assumption rechecking time. First, what is the specific Job implementation?
$job | Get-Member -View Base
#TypeName: Microsoft.Management.Infrastructure.CimInstance#root/virtualization/v2/Msvm_StorageJob
Looks like this is an Msvm_StorageJob, let’s see if it’s implementation of RequestStateChange has any juicy details!
The good news is that we have the return codes, the bad news is that they don’t have any descriptions or names. Let’s add a new assumption for a minute and assume that the GetError method’s return values apply to the RequestStateChange method. Now our errors mean Invalid Parameter
and Invalid State
although that doesn’t get us very far.
Code | Description |
---|---|
Failed | (32768) |
Access Denied | (32769) |
Not Supported | (32770) |
Status is unknown | (32771) |
Timeout | (32772) |
Invalid parameter | (32773) |
System is in used *[sic]* | (32774) |
Invalid state for this operation | (32775) |
Incorrect data type | (32776) |
System is not available | (32777) |
Out of memory | (32778) |
Method Parameters Checked - Transition Started | (4096) |
Invalid State Transition | (4097) |
Use of Timeout Parameter Not Supported | (4098) |
Busy | (4099) |
Alright, maybe it is an invalid parameter. Possibly the .Net DateTime to CIM_DATETIME conversion getting botched? Let’s see if the same thing happens using WMIExplorer.
Good News: Calling convention in powershell at least matches the WMI Explorer results. Well, we have another option. The New Disk Wizard IS cancellable so let’s see if we can’t reverse engineer how it works. First up we’re going to do a simple WMI trace using event viewer Using these instructions and we see multiple events for RequestStateChange on Msvm_StorageJob instances, hmm…
CorrelationId = {950ABF70-EA9B-4722-8E07-B77566C28E44}; ProcessId = 57856; Protocol = DCOM; Operation = MI_Session::Invoke; User = NULL; Namespace = root\virtualization\v2
---
CorrelationId = {950ABF70-EA9B-4722-8E07-B77566C28E44}; GroupOperationId = 117375; OperationId = 117376; Operation = Start IWbemServices::ExecMethod - root\virtualization\v2 : \\.\ROOT\virtualization\v2:Msvm_StorageJob.InstanceID="7C8C39C1-03ED-456B-9A31-1B7A456BA347"::RequestStateChange; ClientMachine = [REDACTED]; User = [REDACTED]\[REDACTED]; ClientProcessId = 57856; NamespaceName = 133044712970425349
---
CorrelationId = {950ABF70-EA9B-4722-8E07-B77566C28E44}; GroupOperationId = 117375; OperationId = 112244; ClassName= Msvm_StorageJob; MethodName = RequestStateChange; ImplementationClass = Msvm_StorageJob; ClientMachine = [REDACTED]; User = [REDACTED]\[REDACTED]; ClientProcessId = 57856; NamespaceName = \\.\root\virtualization\v2
---
ProviderInfo for GroupOperationId = 117375; Operation = Provider::ExecMethod - VmmsWmiInstanceAndMethodProvider : Msvm_StorageJob.InstanceID="7C8C39C1-03ED-456B-9A31-1B7A456BA347"::RequestStateChange; HostID = 2188; ProviderName = VmmsWmiInstanceAndMethodProvider; ProviderGuid = {0c172fd4-1b2a-11da-994c-0008744f51f3}; Path =
---
Stop OperationId = 117376; ResultCode = 0x0
Unfortunately that didn’t show me the parameters (which makes sense, in WMI method parameters are packed into an object) but it does verify that we’re going about it the right way. To find the parameters we’re going to have to bust out API Monitor.
Aside I just want to point out how amazingly handy this tool is. It goes much deeper than procmon/sysmon/WPA/WPP traces (unless you somehow manage to get the Microsoft private symbols/TMFs) and has a ton of accellerators that make ‘getting the job done’ faster than if you have to bust out dnSpy/dotpeek/windbg/ida/etc. In this case it’s automatically capturing the contents of the objects in the parameter pointers and saving them. No manual breakpoint necessary. I’ve even used API Monitor to hijack a firmware update tool and edit it’s ioctls on the fly to force it to update a device that the OEM didn’t want updated. Long story.
Aside-Aside Sorry for the zoom & swoop effect, middle ground between “open full image in new tab” and “fancy overlay with buttons and stuff”.
You can see the documentation on how you pass parameters to a WMI Method here but the gist is that the wizard uses GetMethod to have WMI build you the __PARAMETERS object and PutMethod to populate the properties, then when ExecMethodAsync is called you just give it the pointer to your populated object. API monitor lets you see what values the Wizard is packing into that object before the call happens.
A quick analysis shows that mmc is building the request with RequestedState of 4 and a TimeoutPeriod of null. I’m totally certain that we tried that earlier (Narrator: We didn’t.) it must be something else going on. Let’s see if we can manually cancel a VHD creation started from the wizard!
$tjob = Get-CimInstance -Namespace root\virtualization\v2 -ClassName CIM_ConcreteJob -Filter 'InstanceID = "GUID-INSTANCE-ID-FROM-MMC"'
$tjob | Invoke-CimMethod -MethodName RequestStateChange -Arguments @{RequestedState = 4;TimeoutPeriod = $null}
ReturnValue PSComputerName
----------- --------------
4096
4096 isn’t listed as a storage job result but looking at the CIM_ConcreteJob docs that’s Method Parameters Checked - Transition Started (4096)
! It worked! So why can’t we cancel the VHD creation started via powershell? The two CIM jobs appeared identical!
Oh no…
Oh no…
Guess what combination of RequestedState’s and TimeoutPeriod’s we didn’t try?!
$jobc = Get-CimInstance -Namespace root\virtualization\v2 -ClassName CIM_ConcreteJob -Filter 'InstanceID = "D65CC6A8-E7B4-45E4-A13D-CF7BAD642912"'
$jobc | Invoke-CimMethod -MethodName RequestStateChange -Arguments @{RequestedState = 4;TimeoutPeriod = $null}
ReturnValue PSComputerName
----------- --------------
4096
If I had tried a few different parameter combinations off the bat (honestly there weren’t that many) I would’ve probably figured this out 90 minutes ago. On the other hand, if this had been documented in any way I wouldn’t have had to guess all of that.
# Step 1. Find the InstanceID of your job
Get-CimInstance -Namespace root\virtualization\v2 -ClassName CIM_ConcreteJob | select InstanceID, Caption, Description, Name, Status, StatusDescriptions, JobStatus, PercentComplete, Cancellable
#I only had one job running so it was easy enough to figure out.
InstanceID : D65CC6A8-E7B4-45E4-A13D-CF7BAD642912
Caption : Virtual Hard Disk Creation
Description : Creating Virtual Hard Disk
Name : Virtual Hard Disk Creation
Status : OK
StatusDescriptions : {Job is running}
JobStatus : Job is running
PercentComplete : 7
Cancellable : True
# Step 2. Grab and cancel the Job
$jobc = Get-CimInstance -Namespace root\virtualization\v2 -ClassName CIM_ConcreteJob -Filter 'InstanceID = "D65CC6A8-E7B4-45E4-A13D-CF7BAD642912"'
$jobc | Invoke-CimMethod -MethodName RequestStateChange -Arguments @{RequestedState = 4;TimeoutPeriod = $null}
If the answer to all of the above questions is yes, then you’re as desperate as I was and there’s a small chance I have the answer for you! I’m supposed to advise you to do the ‘supported thing’ which is reinstall windows… but if you’re reading this then you probably don’t care!
Fortunately for you this isn’t Microsoft “Answers” and I have two ways to get the page working: skip to Option A or B if you’re feeling bold.
At first I thought that maybe it was a JavaScript issue, maybe bad zone security settings, etc. I used fiddler to scrape the URL that office was trying to load for the sign in and found something along the lines of “https://odc.officeapps.live.com/odc/v2.1/hrd” (plus a bunch of queries) and bingo! This was the page that WAM was trying to load. It works in every browser *except* IE… which happens to be the engine that O365 apps use to render the login page depending on OS build, App build, and whether Mecury is in retrograde.
Note: You can/should check here to see which engine the sign-in modal might be using.
Digging further I saw a bunch of strange issues in the JS console and on a whim changed the emulation settings. You can test this yourself pretty easily:
iexplore.exe https://odc.officeapps.live.com/odc/v2.1/hrd
If the prompt suddenly works then I think we’re on the same page?! If you look in the source you can see that Microsoft specifies that it wants IE to use IE10 mode with the x-ua-compatible
tag. I’ll be damned if I know why they set that, but I *do* know that IE11 mode is the one that actually works.
So now we’re sophomores: We’ve learned that IE is trying (and failing) to render this in IE10 mode and that it works correctly in Edge mode. We are still foolish as to the reason this is only happening to us and not a few million other companies (my first guess would probably be something vague like “Borked IE registry”). Fortunately we’re now wise to a potential workaround: force the embedded IE window to render the page in Edge mode.
We’re going to use a registry flag as per MSDN to tell IE that it should ignore all hints and render everything that your target application requests in IE11 mode.
HKLM\SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION
REG_DWORD
with the name of your chosen app (e.g. OneNote.exe), set the value to 0x00002af9 (11001)
.Well, just use Fiddler (Or Charles) and edit the contents of the page before it lands in the target application. We’re going to change the hint from content="ie=10"
to content="EdgeHTML
” which will tell IE to render the page in IE11/Edge mode.
OnBeforeResponse
function.oSession.utilReplaceInResponse("ie=10","EdgeHTML");
One one hand this doesn’t fix the root cause because I don’t know what the root cause ACTUALLY IS. On the other hand it does finally let you sign into your 365/Visual Studio apps. If you have any ideas what the heck is going on please do share!
]]>Run-time error ‘1004’: unable to set the PageBreak property of the Range class
Turns out that when applied to my test data the PrintArea of the print layout was set so that the first row I was trying to add a page break to was also the first row of the PrintArea. I guess it makes sense but that isn’t documented anywhere.
So if you’ve been trying a dozen different ways of adding a page break, do yourself a favour and double check the area selected for printing. If you’re trying to add a page break on the border of your selection it’ll fail.
]]>Note: The following isn’t normally a good practice for a lot of reasons. In fact (minus the G/MSA account) it’s practically a low-effort malware persistence technique. Normally you’d want a repository style folder so that you can view/track the scripts (and potentially include them in your CD pipelines).
This could be appropriate if your already-change-controlled scripts need to create temporary sub-tasks/jobs. Even then, SECOPS will probably raise their eyebrows at you for suggesting it.
Sometimes I want to schedule a script to run with specific domain credentials/service account without storing credentials locally. With G/MSA’s an attacker needs to use AD/LDAP to steal the G/MSA password blob, which is easier to log and identify centrally. Credentials saved to a scheduled task can be really easily extracted with something like NetPass.
Anyways, digressing from the caviats: here I wanted to schedule maintenance notifications to users that have been logged in for so long that their hosts are up for replacement as part of our lifecycle automations, but wanted to put together a generic helper utility for creating these tasks.
You can’t select a G/MSA account in the Task Scheduler UI, only with SC or PowerShell; and since nobody like a process that reads “Do a dozen things by hand in the UI, then write some lines to modify it” I figured the generic helper would be a lot more useful.
The next step to low footprint bliss is to say goodbye to all the files and ACLs! Inline the scripts (if they’re short enough)! Here are the key parts for a file-less, sort-of-credential-less PowerShell scheduled task. In this case it just schedules desktop messages for Citrix sessions. Ironically this specific example drops transcript copies but you get the point.
(Sorry for not sharing the full helper, but these are the guts of it. Pretty simple to generalize based on your needs. Besides, I’d feel bad if someone out there is B64 encoding all of their maintenance scripts.)
#Encode script as Base64, send a Citrix message in this example
function EncodeMessageTaskScript ($MessageText, $AdminAddress)
{
$TaskScript = @"
Start-Transcript "C:\ScriptLogs\ScheduledPS.log" -Append
& { Add-Pssnapin @('Citrix.Host.Admin.V2','Citrix.Broker.Admin.V2')}
`$CurrentSessions = Get-BrokerSession -AdminAddress "$AdminAddress" -MaxRecordCount 1000 | ? DesktopGroupName -eq 'Nope'
`$CurrentSessions | % { Send-BrokerSessionMessage -AdminAddress "$AdminAddress" -InputObject `$_ -MessageStyle Critical -Text "$MessageText"`nMessage Sent [`$(Get-Date)]" -Title "Maintenance Warning"}
Stop-Transcript
"@ #Let's pretend that this is indented...
[Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($TaskScript))
}
function CreateScheduledGMSATask ($EncodedPsScript, [datetime]$TriggerDateTime, $TaskName)
{
$Action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-EncodedCommand `"$EncodedPsScript`" -NoLogo -NoProfile -ExecutionPolicy Bypass"
$Principal = New-ScheduledTaskPrincipal -LogonType Password -RunLevel Limited -UserId 'DOMAIN\[G]MSA$'
$Settings = New-ScheduledTaskSettingsSet -Compatibility Win8
$Trigger = New-ScheduledTaskTrigger -Once -At $TriggerDateTime
$TaskObj = New-ScheduledTask -Action $Action -Principal $Principal -Trigger $Trigger -Settings $Settings
Register-ScheduledTask -TaskName $TaskName -InputObject $TaskObj
}
$EncodedReminderTask = EncodeMessageTaskScript -MessageText "Your Message Text" -AdminAddress $CitrixAdminAddress
CreateScheduledGMSATask -EncodedPsScript $EncodedReminderTask -TriggerDateTime $MaintReminder -TaskName "MessageTask $($MaintStartTime.ToString('yyyyMMdd.HHmmss'))"
-RecoveryLocation CopyToFolder -RecoveryType Restore
.
-RecoveryType
Specifies the recovery type. If you specify the HyperVDatasource parameter, the only valid value is Recover. The acceptable values for this parameter are: Recover or Restore.
The Microsoft documentation is flat out wrong. It very explicitly states that the only valid RecoveryType
for HyperVDatasource
is Recover
. When trying to recover to an alternate disk location their example does not work. Based on the example script you would expect the code below to work. Instead if you try it you’ll get a powershell error stating "The recovery point location that you have passed is invalid. Please try again with a different value (ID:31050)."
$BadOption = New-DPMRecoveryOption -HyperVDatasource -TargetServer "target.contoso.com" -RecoveryLocation CopyToFolder -RecoveryType Recover -TargetLocation "D:\DestinationFolder"
So maybe instead of that you’d google around then try the following (obviously wrong) option AlternateHyperVServer
out of desperation and it appears to work… At first.
$BadOption = New-DPMRecoveryOption -HyperVDatasource -TargetServer "target.contoso.com" -RecoveryLocation AlternateHyperVServer -RecoveryType Recover -TargetLocation "D:\DestinationFolder"
However at some point that job will fail with "An unexpected error occurred while the job was running. (ID 104 Details: Unknown error (0x80041002) (0x80041002))"
which is entirely unhelpful. If you go to the job details you’ll get an equally unhelpful error 3111
. Making some assumptions around that error code (WMI object not found error
) I’m thinking that it’s trying to import the VM to a hyper-v instance running on that server. That doesn’t work if there’s no valid hypervisor running. Instead you need to user the parameters -RecoveryLocation CopyToFolder
and -RecoveryType Restore
.
$WorkingOption = New-DPMRecoveryOption -HyperVDatasource -TargetServer "target.contoso.com" -RecoveryLocation CopyToFolder -RecoveryType Restore -TargetLocation "D:\DestinationFolder"
The Well Architected Framework (https://docs.microsoft.com/en-us/azure/architecture/framework)
The Cloud Adoption Framework (https://docs.microsoft.com/en-us/azure/cloud-adoption-framework)
Joel On Software (https://www.joelonsoftware.com)
Azure Architecture Templates (https://docs.microsoft.com/en-us/azure/architecture/browse)
Azure AD Teams and Groups drawings (https://docs.microsoft.com/en-us/microsoftteams/teams-architecture-solutions-posters#groups-in-microsoft-365)
SANS/ISC Daily Security Briefing (https://podcasts.apple.com/us/podcast/sans-internet-stormcenter-daily-cyber-security-podcast/id304863991)
The Twelve-Factor App (https://12factor.net)
The Eight Fallacies of Distributed Computing (http://nighthacks.com/jag/res/Fallacies.html)
Archived Ten Laws of Security 2.0 (https://web.archive.org/web/20180529154650/https://technet.microsoft.com/en-us/library/hh278941.aspx)
Archived Ten Laws response (https://web.archive.org/web/20190928204316/http://www.edgeblog.net/2006/10-new-immutable-laws-of-it-security)
Archived Ten Laws Re-Review (https://web.archive.org/web/20190710001511/https://docs.microsoft.com/en-us/previous-versions/technet-magazine/cc895640(v=msdn.10))
Krebs on Security (https://krebsonsecurity.com)
Raymond Chen’s Blog (https://devblogs.microsoft.com/oldnewthing)
Barracuda Spam Firewall Rooting (http://blog.shiraj.com/2009/09/barracuda-spam-firewall-root-password)
Group Policy team blog (https://blogs.technet.microsoft.com/grouppolicy)
Aaron Stebner’s Weblog (notes on .Net) (https://docs.microsoft.com/en-ca/archive/blogs/astebner)
AskPerf Ask The Performance Team (https://aka.ms/AskPerf)
AskDS Ask the Directory Services Team (https://aka.ms/AskDS) (Archive: https://docs.microsoft.com/en-ca/archive/blogs/askds) (A lot of interesting deep dives on ESE)
Thomas Maurer’s Blog (Azure Advocate) (https://www.thomasmaurer.ch)
Carl Stalhood’s EUC Blog (https://www.carlstalhood.com)
Robin Hobo (https://www.robinhobo.com)
Helge Klein’s Blog (https://helgeklein.com)
Brent Ozar’s Corp Blog (https://www.brentozar.com)
DBA Reactions (Lighthearted fun) (https://dbareactions.com)
]]>Error (23008)
The VM BlahBlahBlah cannot be migrated to Host BlahHost.contoso.ads due to incompatibility issues. The Virtual Machine Management Service failed to establish a connection for a Virtual Machine migration with host ‘BlahHost.contoso.ads’: No credentials are available in the security package (0x8009030E).
klist purge -li 0x3E7
to clear the computer account tickets on each host and it will start working. No point disabling a security feature out of impatience.