Azure Europe Region Account How to Run Startup Scripts on Azure VM

Azure Account / 2026-05-16 22:01:00

Introduction: The Joy (and Horror) of Boot-Time Automation

So you want your Azure VM to do something when it starts up. Maybe it should install a package, register with a monitoring tool, pull configuration files, start a service, or greet the world with the confidence of a well-adjusted cloud wizard. Great. This is exactly what “startup scripts” are for—except, in practice, boot-time automation can feel like training a raccoon to do taxes: possible, but you should expect occasional chaos.

In Azure, there are several ways to run code at startup, depending on the VM’s operating system, your preferred level of control, and how you want to manage logs, credentials, and retries. The key idea is simple: you want an automated mechanism that triggers when the VM boots, runs your script, and behaves predictably even if the VM reboots, the network is slow, or the script takes longer than you hoped.

This article focuses on the most common and reliable patterns for running startup scripts on Azure Virtual Machines. We’ll talk about Linux and Windows separately where it matters, but the principles—idempotency, logging, least privilege, and sanity checks—apply everywhere.

What “Startup Script” Means in Azure

When people say “startup script,” they usually mean one of these:

  • Code that runs automatically when the VM first boots (or after every reboot).
  • Code that runs during provisioning or deployment (near boot time, but not necessarily on every reboot).
  • A configuration step triggered by an Azure-managed extension or agent.

In Azure, the most practical, repeatable approach is often to use a VM extension (like the Custom Script Extension). For Linux, cloud-init is also extremely common and powerful. For Windows, VM extensions are typically the go-to for straightforward “run this script at startup” behavior.

One quick reality check: startup scripts are not magic. They run in an environment that can be slightly chaotic: the network may not be fully ready, disks might not be mounted yet, packages might need time, and any external dependencies (like downloading artifacts from storage) can fail. Your script should handle all of that with the confidence of someone who’s been burned before (because you will be, and then you’ll be wiser).

Azure Europe Region Account Before You Begin: A Checklist That Saves You Later

Before you write any script, do this mental checklist. It’s not glamorous, but it prevents the classic “it worked once” problem.

1) Decide whether you want “run once” or “run every boot”

If your script installs packages and configures services, you might want it to run on every boot but be idempotent (meaning it can run multiple times without breaking things). If you truly want it to run once, you’ll need a marker file or another mechanism to skip subsequent runs.

2) Make your script idempotent

Azure Europe Region Account Idempotency means:

  • Re-running won’t duplicate cron jobs, append configuration lines over and over, or reinstall everything unnecessarily.
  • Your script checks existing state before changing it.

Example: If you add a systemd service file, verify it exists and contains the expected configuration before replacing it.

3) Think about where credentials come from

Don’t hardcode secrets in your script. Use Azure Key Vault and managed identities where possible, or pass secrets securely through Azure mechanisms. Even better: avoid needing secrets at startup at all—then you can sleep.

4) Plan for logging

If you can’t see what happened during startup, you’re basically playing “guess the failure” with your production environment. Ensure your script logs to a predictable file location and that errors are captured.

Option 1: Using Azure VM Extensions (Most Common, Very Reliable)

Azure VM Extensions are agents and handlers that run inside the VM to perform specific tasks. One widely used extension is the Custom Script Extension, which can download and execute scripts during VM provisioning or on startup (depending on how you configure it).

This option is popular because it’s consistent across deployments and integrates nicely with Azure tooling. If you’re building repeatable infrastructure, VM extensions tend to be your best friend.

Using Custom Script Extension on Linux

Custom Script Extension can:

  • Download your script from a source (like an Azure Storage container).
  • Execute it using a specified command.
  • Capture output to logs.

The basic pattern looks like this:

  • Host your script somewhere reachable (commonly a storage blob).
  • Create the VM (or configure the extension) via Azure CLI, ARM template, or Terraform.
  • The extension runs your script at the appropriate time.

Important note: “Startup” timing can vary by extension configuration and VM lifecycle. Often, this will run during provisioning rather than literally on every boot. If you need “every boot,” you can combine extension execution with your own mechanism inside the VM (like a startup hook that runs every time).

Example: Linux script that’s safe to re-run

Let’s sketch a simple Linux script conceptually. Imagine you want to install a package and configure a service. Your script should check whether the package exists and whether configuration is already in place.

Here’s the general idea (not tied to a specific extension syntax yet):

  • Update package lists if needed.
  • Install a package only if it’s missing.
  • Write config only if it differs.
  • Enable and restart the service.

That way, reboots don’t cause repeated work or weird duplication.

Using Custom Script Extension on Windows

For Windows VMs, the Custom Script Extension works similarly: it downloads and executes a script (often PowerShell) inside the VM. Windows startup scripts can also be scheduled via Task Scheduler, but using the extension can be easier and more repeatable from infrastructure-as-code.

As with Linux, your script should:

  • Log its progress to a file.
  • Validate state before changing it.
  • Exit with a non-zero code when something fails (so the extension marks it as failed).

Option 2: cloud-init on Azure Linux VMs (The “Native” Way for Linux)

If your VM is Linux, cloud-init is the elegant, cloud-native approach. cloud-init can run “user-data” at first boot, allowing you to perform initialization tasks. It’s designed for exactly this: configure instances during early lifecycle.

cloud-init uses configuration formats (usually YAML) and can run scripts and modules during boot. It’s especially useful if you’re deploying many VMs and want consistent initialization.

However, one should not assume that cloud-init will run your script on every reboot. cloud-init typically runs on first boot (though it can be configured for other behavior). If you need every reboot, you may still use a system mechanism like systemd or cron to repeat the task.

Example: cloud-init concepts for running a script

With cloud-init, you typically provide:

  • A script to run at boot (cloud-init “runcmd” or “bootcmd” style modules).
  • Package installation instructions (via package modules).
  • Files to write to disk (via write_files module).

You combine these to create a boot-time recipe. That recipe is then applied during the VM’s initialization.

If you’re experienced with Linux administration, cloud-init will feel like a structured version of what you’d manually do after SSHing into a fresh VM—only with more consistency and fewer “oops I forgot to install that” moments.

Option 3: Native Startup Mechanisms (systemd for Linux, Task Scheduler for Windows)

Sometimes you don’t want a VM extension or cloud-init. Maybe you just want a reliable startup trigger inside the OS. That’s valid. In fact, if you need strict “run on every boot,” native mechanisms can be the cleanest.

Linux: systemd service or timer

On modern Linux distributions, systemd is the king of “run this at boot.” You can create a systemd service that runs at startup. You can also use a systemd timer for more complex schedules (like running every 10 minutes, or daily).

To do this, you would:

  • Create a service unit file that describes your script.
  • Enable the service so it runs at boot.
  • Ensure the script logs and exits properly.

systemd also gives you a nice place to look at failures: journal logs. That’s like having the VM tell you what went wrong instead of making you debug in the dark.

Linux: cron @reboot (quick but sometimes less elegant)

You can also use cron with @reboot. It’s simple, but systemd is typically preferred for better control and logging. Cron works fine for lightweight tasks, but for more serious automation, systemd often wins.

Windows: Task Scheduler at startup

On Windows VMs, you can create a Scheduled Task triggered “At startup.” This runs your PowerShell or batch script whenever the machine boots.

In this approach, you typically:

  • Write your PowerShell script.
  • Create a scheduled task with the proper trigger.
  • Run the task under the correct user context (or use a managed identity pattern to avoid storing credentials).
  • Log output to a file so you can troubleshoot.

One gotcha with Windows startup tasks: timing. Some services and networking might not be ready immediately at startup. A scheduled task might run before you can reach storage or dependencies. In such cases, add retries or delays, or configure the task to run after the network is available (as best as the OS allows).

Which Option Should You Use?

Azure Europe Region Account Here’s a practical decision guide:

  • If you want a deployment-friendly approach that fits infrastructure-as-code: use Azure VM Extensions.
  • If you’re deploying Linux instances and want native first-boot initialization: use cloud-init.
  • Azure Europe Region Account If you need the script to run on every boot with strong OS-native control: use systemd (Linux) or Task Scheduler (Windows).

Many real-world setups use a combination: an extension runs once to install a prerequisite agent, then the agent or systemd handles recurring startup tasks.

Common Pitfalls (The Stuff That Breaks at 2 AM)

Let’s talk about the most common ways startup scripts fail. Spoiler: it’s rarely the script itself. It’s usually the environment around it.

Pitfall 1: The script assumes networking is ready immediately

At startup, networking might not be ready, or DNS might be slow. If your script downloads from storage or reaches an API endpoint, add retry logic. For example:

  • Azure Europe Region Account Try download up to N times with short delays.
  • Azure Europe Region Account Fail gracefully with clear logs if it never succeeds.

Pitfall 2: Permissions issues

Common examples:

  • Linux script runs but can’t write to system directories.
  • Windows script runs under a user account that doesn’t have rights.
  • The script downloads a file but can’t execute it due to execution policies.

Fix by using the correct execution context and verifying file permissions before execution.

Pitfall 3: You edit the file wrong (line endings, encoding, path differences)

PowerShell scripts, shell scripts, and artifact files can behave differently based on encoding and line endings. A script created on Windows with CRLF might still run on Linux depending on the interpreter, but it’s not always fun. Ensure your scripts are created with consistent encoding and test them in the same environment you deploy.

Pitfall 4: Not capturing logs

Azure Europe Region Account If you don’t log, the failure becomes a ghost story. Always:

  • Write a log file to a known path.
  • Echo commands (or use verbose mode).
  • Include timestamps.
  • Capture stderr.

Pitfall 5: Your script is not idempotent

If it appends configuration to a file every time, you’ll eventually build a configuration file so thick it becomes its own firewall. Use state checks and marker files.

Logging and Troubleshooting: How to Know What Actually Happened

When a startup script fails, you need answers. Where you look depends on your approach.

For VM extensions

Typically, extension logs are stored in specific paths and also can be collected through Azure’s logging mechanisms. The exact paths can vary, but the general idea is:

  • Check extension output logs.
  • Check your script’s own log file.
  • Confirm that the script ran with expected parameters.

For cloud-init

cloud-init stores logs and status information that help you determine what modules ran and whether any commands failed. Check the cloud-init logs and instance status.

For systemd (Linux)

Use journal logs. systemd makes it pretty straightforward to inspect failure reasons and exit codes.

For Task Scheduler (Windows)

Check the Task Scheduler history and script output logs. If your script writes to a file, you’ll thank yourself immediately.

Best Practices: How to Make Startup Scripts Behave Like Adults

Here are practical best practices that make your automation calmer and more predictable.

1) Use strict error handling

On Linux shell scripts, use strict modes (like setting the shell to exit on errors). On PowerShell, use robust error handling and ensure the script exits with an appropriate code.

2) Add retries for external dependencies

Any dependency that could fail transiently (DNS, storage downloads, API calls) should be retried. Don’t retry forever; retry responsibly, and log each attempt.

3) Use configuration management mindset

Instead of “do X” blindly, think “ensure X is in the desired state.” For example:

  • Ensure package is installed (not just “apt-get install happened”).
  • Ensure service is running with the correct configuration.
  • Ensure firewall rules exist (not “we ran ufw once”).

4) Separate concerns

Large scripts can become spaghetti faster than you can say “why is this 800 lines long.” Consider splitting tasks into smaller scripts or functions, so you can test them independently.

5) Keep scripts versioned

Host scripts in a versioned location (for example, a storage blob with a versioned name, or a deployment pipeline that updates the artifact). That way, when you see a failure, you know which script version caused it.

Putting It Together: A Reusable Startup Script Pattern

Let’s define a pattern you can adapt across Linux and Windows.

The pattern

  1. Create a log file and write “startup script begin” with timestamp.
  2. Load configuration parameters (environment variables or secure parameters).
  3. Check prerequisites (network, disk space, required tools installed).
  4. Perform tasks with state checks (idempotent operations).
  5. Restart or enable services if necessary.
  6. Write “startup script complete” with success/failure status.
  7. Exit with an appropriate status code.

Because when this works, it should also be easy to explain, easy to troubleshoot, and easy to reuse.

Sample Scenarios (So It’s Not Just Theory)

Let’s talk about a few real scenarios and how you might implement startup scripting for each.

Scenario A: Installing an agent on boot

You have a monitoring or security agent you want on every VM. The script:

  • Downloads the installer from a controlled source.
  • Installs the agent.
  • Configures it using environment variables or managed identity-based auth.
  • Starts the agent service.

Recommended approach: VM extension for initial setup plus system service to ensure it stays running, or cloud-init if you’re doing first boot provisioning only.

Scenario B: Pulling application configuration

Your VM needs application config from storage or Key Vault. At startup, your script:

  • Fetches configuration files.
  • Validates config integrity (optional but useful).
  • Writes files to expected directories.
  • Reloads application services.

Recommended approach: run on boot via systemd/Task Scheduler, especially if configuration updates may happen between reboots.

Scenario C: Running a one-time migration

You need a script that runs once per VM deployment (like database migrations or initial setup). You want it to run once and never again.

Recommended approach:

  • Use a marker file check (Linux) or registry/task flag (Windows).
  • Log the migration version applied.
  • Fail fast if migration is required and cannot proceed.

Infrastructure-as-Code Friendly Setup

If you’re using Terraform, Bicep, or ARM templates, you can attach VM extensions or cloud-init configuration directly as part of your VM definition. This ensures the startup behavior is part of the infrastructure description, not a manual afterthought.

When done well, your VM creation pipeline becomes a repeatable process:

  • Create VM.
  • Attach extension or configure cloud-init.
  • Run scripts automatically.
  • Validate results through logs and health checks.

It’s like ordering a pizza: you don’t want to hand the pizza chef the ingredients and instructions every time. You want the process baked in.

Security Considerations (Because “Just Run This Script” Has Consequences)

Startup scripts often touch sensitive infrastructure: secrets, authentication tokens, service endpoints, and privileged system configuration. Treat them like you’d treat a locksmith’s tools—important and not for random strangers.

Use managed identities where possible

Instead of storing credentials inside scripts, use managed identities to access Key Vault or other Azure resources. This reduces secret exposure and improves auditability.

Keep scripts minimal and reviewable

Large scripts with lots of network calls and file operations are harder to audit. Keep them focused on their job.

Validate downloaded artifacts

If you download installers or packages, consider verifying checksums or signatures where feasible. At minimum, ensure downloads come from trusted sources and correct URLs.

End-to-End: A Practical Approach You Can Adopt Today

Here’s a simple, practical strategy that works in many teams:

  1. Use cloud-init or a VM extension to perform initial prerequisites at first boot (install dependencies, create service accounts, lay down baseline config).
  2. Use systemd or Task Scheduler for ongoing startup tasks that must run on every boot.
  3. Azure Europe Region Account Make scripts idempotent and log everything.
  4. Validate behavior using automated checks (like ensuring the service is running after boot).

This layered approach reduces the chance that your VM is “almost configured” after a reboot, which is the cloud equivalent of showing up to a party with your shoes on but your shirt inside-out.

Frequently Asked Questions

Does Azure guarantee startup scripts run every time the VM reboots?

Not automatically in every approach. cloud-init commonly runs on first boot, while VM extensions may run during provisioning or according to configuration. If you need guaranteed behavior on every reboot, use OS-native startup triggers (systemd/Task Scheduler) or a mechanism you explicitly configure to run each boot.

Where can I find logs for my startup scripts?

Check extension logs (for VM extension approaches), cloud-init logs (for cloud-init), systemd journal (for systemd), or Task Scheduler history/output logs (for Windows scheduled tasks). Also write your own script logs to a known file path.

What if my script fails—will Azure retry it?

Behavior depends on the extension and configuration. Some mechanisms might not automatically retry indefinitely. Your best defense is to implement retries for transient failures and ensure clear error handling. If your script fails due to a permanent issue, retrying blindly only makes you late to the problem party.

Should my startup script be long?

Try to keep it reasonably sized and modular. Long scripts are harder to troubleshoot and more likely to run into timeouts or environmental readiness issues. Break complex workflows into smaller steps.

Conclusion: Startup Scripts, But With Confidence

Running startup scripts on Azure VMs is absolutely doable, and once you set it up with the right approach, it becomes one of those “why didn’t we do this earlier?” wins. The main thing is to choose the right mechanism: VM extensions for a deployment-friendly pattern, cloud-init for Linux first-boot initialization, and systemd/Task Scheduler for reliable every-boot behavior.

Then, add the boring-but-powerful stuff: idempotency, logging, retries for external dependencies, and secure credential handling. Your future self will send you a thank-you note from a timeline where everything boots correctly the first time.

Now go forth and make your Azure VMs act like they know what they’re doing. Because they do. They just need a script, a plan, and a little patience at boot time.

TelegramContact Us
CS ID
@cloudcup
TelegramSupport
CS ID
@yanhuacloud