agentsclimarketplace

Azure support ticket

Skill tarujg/azure-support-ticket/skills/azure-support-ticket

File and manage Azure support tickets from the terminal via the az CLI support extension. Use when the user wants to open, draft, list, inspect, or follow up on an Azure support case - "file an Azure support ticket", "open a case with Microsoft", "raise a ticket for this Azure issue", "check my Azure support tickets", "reply on that support case".From its SKILL.md

Install
npx -y skills add tarujg/azure-support-ticket --skill azure-support-ticket

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

11.2 KB, as published. Nobody here has run it

You are filing an Azure support ticket via the az CLI. Filing a ticket is a one-way door and it goes to an external vendor, so the job is not "run the command" - it is "assemble a ticket support can actually act on, then get explicit approval before submitting".

Hard rules

  1. Never invent a number, timestamp, region, or resource ID. If a value is not supplied by the user or read from a real source, leave it as an explicit [FILL] slot and ask. A ticket with fabricated metrics is worse than no ticket - it sends Microsoft chasing the wrong thing.
  2. Never run tickets create without showing the assembled command and getting a clear yes. Draft, display, confirm, then submit.
  3. Never guess the problem-classification. Resolve it programmatically - clone it off a prior ticket (step 3) or look it up via step 4. Azure's GUIDs change; a stale hardcoded one silently misroutes the ticket.
  4. az login is the user's job. It needs a browser. Ask them to run it, don't try to automate it.

Preflight

az extension add --name support     # idempotent
az account show                     # confirms login; errors if not authed

If not logged in, stop and ask the user to run az login themselves. In Claude Code, tell them to prefix with ! so the output lands in the conversation:

! az login

Then list what the account can see and pin the subscription:

az account list -o table
az account set -s <SUBSCRIPTION-ID>

Run the list even when defaulting - it is the cheapest confirmation that the login landed on the right tenant and that the target subscription is actually visible. If it is missing from the list, the login is on the wrong tenant and everything downstream will fail with confusing errors.

Defaults

Most people file every ticket on the same subscription. Don't stop to ask which one - resolve it in this order:

  1. $AZURE_SUPPORT_SUBSCRIPTION, if set
  2. the subscription already active in az account show

Only ask when az account list shows several and the request names a resource that clearly lives in a different one.

If $AZURE_SUPPORT_REF_TICKET is set, it names a prior ticket to clone the classification from - this skips the step 4 catalog hunt entirely:

PC=$(az support in-subscription tickets show \
       --ticket-name "$AZURE_SUPPORT_REF_TICKET" \
       --query problemClassificationId -o tsv)

Fall back to step 4 only when the new ticket is for a different Azure service than the reference.

Step 1 - Establish what kind of ticket

Four kinds, each with different required extras:

KindExtra fields needed
Technical--technical-resource (full ARM resource ID) - optional but speeds triage a lot
Quota--quota-change-version, --quota-change-subtype, --quota-change-requests
Billingnone beyond the base set
Subscription managementnone beyond the base set

For quota tickets, read the current numbers first - az quota list --scope <scope> or az vm list-usage --location <region> -o table - so the request states a real baseline instead of a guess. This skill files the change request; it does not read quotas. Many quotas are self-service and need no ticket at all, so check that before filing.

Step 2 - Gather the data

Base required set for every ticket:

--ticket-name                    slug, alphanumeric + dashes, unique in the subscription
--title                          one line
--description                    the body (see template below)
--severity                       minimal | moderate | critical | highestcriticalimpact
--problem-classification         full ARM path, see step 4
--advanced-diagnostic-consent    Yes | No
--contact-first-name             --contact-last-name
--contact-email                  --contact-method  email | phone
--contact-country                ISO 3166-1 alpha-3, e.g. USA
--contact-language               e.g. en-us
--contact-timezone               Microsoft Time Zone Index name, e.g. "Pacific Standard Time"

Severity notes: critical requires a paid support plan. highestcriticalimpact is Premium-only - do not select it speculatively, the ticket will be rejected or downgraded.

Useful optional flags: --start-time (UTC ISO 8601, when the problem began), --contact-additional-emails, --require-24-by-7-response, --no-wait.

If the user needs --contact-method phone, --contact-phone-number becomes required.

Step 3 - Shortcut: clone an existing ticket's classification

If the user points at a prior ticket (often as a portal URL), read it instead of hunting for GUIDs. A portal URL looks like:

https://portal.azure.com/#view/.../id/%2Fsubscriptions%2F<SUB>%2Fproviders%2Fmicrosoft.support%2Fsupporttickets%2F<TICKET-NAME>/...

URL-decode it to recover <SUB> and <TICKET-NAME>, then:

az support in-subscription tickets show --ticket-name <TICKET-NAME> -o json

Reuse problemClassificationId and serviceId verbatim. This is faster and more accurate than searching the service catalog.

Step 4 - Resolve the problem classification

# 1. find the service
az support services list --query "[].{name:name, display:displayName}" -o table

# 2. list its problem classifications - select id, NOT just name
az support services problem-classifications list \
  --service-name <service-guid> \
  --query "[].{id:id, display:displayName}" -o table

Gotcha: --problem-classification is not a bare GUID. It is the full ARM path:

/providers/Microsoft.Support/services/<service-guid>/problemClassifications/<pc-guid>

-o table on a plain list truncates to the short name. Always project the id field as shown above, or you will build an invalid command.

Step 5 - Write the description

Support engineers triage on specifics. A good description answers what, where, when, how much, and what you already ruled out. Template:

<One-paragraph statement of the problem and the production impact.>

Resource / deployment: <name>
Region(s):             <regions>
Onset (UTC):           <timestamp>
Pattern:               sustained | intermittent | load-correlated

Measured impact:
  Baseline: <metric> = <value>
  Current:  <metric> = <value>
  Measured at: <client side | service response headers>

Already ruled out:
  <e.g. throttling - provisioned capacity is N, observed usage is M, no 429s observed>
  <e.g. client-side - confirmed against service-reported latency, not just caller timing>

Sample request IDs (x-ms-request-id): <3-5 IDs from the degraded window>

Ask: <what you want - RCA, mitigation, capacity change, known-issue confirmation>

Gotcha: the description must be ASCII. The API rejects a body containing non-ASCII characters with a vague JsonDeserializationError: Description contains invalid characters and no offset pointing at the culprit. This bites model-written bodies constantly, because em-dashes, curly quotes, and ellipsis characters are exactly what a model produces. Normalize before submitting:

python3 - <<'PY' > /tmp/ticket-body-ascii.txt
import sys, unicodedata
s = open('/tmp/ticket-body.txt', encoding='utf-8').read()
for bad, good in {'—':'-','–':'-','“':'"','”':'"','‘':"'",'’':"'",
                  '…':'...','\xa0':' ','€':'EUR ','£':'GBP ','×':'x'}.items():
    s = s.replace(bad, good)
out = unicodedata.normalize('NFKD', s).encode('ascii', 'ignore').decode()
lost = sorted({c for c in s if not c.isascii() and c not in ('\xa0',)})
if lost:
    print('rewrote or dropped: ' + ' '.join(lost), file=sys.stderr)
print(out, end='')
PY

Read the stderr line. Anything listed there was rewritten or silently deleted - a dropped currency symbol turns "EUR 5000 overcharge" into "5000 overcharge", which changes what you are reporting. Spell such values out in words and re-run rather than shipping the lossy version.

Do not reach for iconv here. Measured on macOS: iconv -f utf-8 -t ascii//TRANSLIT mangles curly quotes (curly "quotes" becomes curly "quo"es ) and exits non-zero, and iconv -c drops the character entirely so em-dash silently becomes emdash. Both corrupt a ticket body in ways support will misread.

For latency or error-rate tickets specifically, Microsoft's first reply is reliably a request for timestamps, region, and x-ms-request-id samples. Collect those up front or expect a wasted round trip.

Before filing a latency ticket, sanity-check that the slowdown is server-side. Client-side timing inflated by caller cancellations, retries, or token-limit truncation is a common false positive and will get the ticket closed as not-reproducible.

Step 6 - Assemble, confirm, submit

Show the user the complete command. Get an explicit yes. Then run it.

az support in-subscription tickets create \
  --ticket-name "<slug>" \
  --title "<title>" \
  --description "<description>" \
  --severity moderate \
  --problem-classification "/providers/Microsoft.Support/services/<svc>/problemClassifications/<pc>" \
  --advanced-diagnostic-consent Yes \
  --contact-first-name <First> --contact-last-name <Last> \
  --contact-email <email> --contact-method email \
  --contact-country USA --contact-language en-us \
  --contact-timezone "Pacific Standard Time"

Long descriptions are awkward to quote inline. Write the body to a file and interpolate:

DESC="$(cat /tmp/ticket-body-ascii.txt)"    # the normalized file from step 5
az support in-subscription tickets create --description "$DESC" ...

On success, report the returned ticket name and the portal URL so the user can track it.

Follow-up commands

# list recent tickets (defaults to last week)
az support in-subscription tickets list -o table

# read one
az support in-subscription tickets show --ticket-name <name> -o json

# change severity / status / contact info (severity is locked while an engineer is assigned)
az support in-subscription tickets update --ticket-name <name> --severity critical

# reply on the case
az support in-subscription communication create \
  --ticket-name <name> --communication-name <slug> \
  --communication-subject "<subject>" --communication-body "<message>"

# read the thread
az support in-subscription communication list --ticket-name <name> -o table

Gotcha: on communication create the subject and body flags are prefixed - --communication-subject and --communication-body, not --subject/--body - while --ticket-name on the same command is not prefixed. Getting this wrong fails with a required- argument error. --communication-sender also exists and is required in some cases.

Attachments go through a file workspace (az support in-subscription file-workspace and az support in-subscription file). Check --help on those groups for the current flag set rather than assuming.

Notes

  • Ticket data is only retrievable for 18 months after creation.
  • az support no-subscription exists for tenant-level issues with no subscription context.
  • --no-wait returns immediately instead of polling the long-running create operation.

Keep looking

Skills are one crate of 628,853. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.