Skip to content

Instantly share code, notes, and snippets.

@skurik
Last active August 12, 2021 11:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save skurik/0379aa55d560306995ad89c2a6a1ad54 to your computer and use it in GitHub Desktop.
Save skurik/0379aa55d560306995ad89c2a6a1ad54 to your computer and use it in GitHub Desktop.
Declarative IaC with Pulumi
public class AlertStack : Stack
{
public AlertStack()
{
var resourceGroup = ResourceGroup.Get(resourceGroupName, resourceGroupId);
var redis = Pulumi.AzureNative.Cache.Redis.Get(redisName, redisId);
var actionGroup = ActionGroup.Get(actionGroupName, actionGroupId);
var redisAlert = CreateRedisMemoryUsageAlert(resourceGroup, redis, actionGroup);
RedisMemoryUsageAlertId = redisAlert.Id;
}
[Output]
public Output<string> RedisMemoryUsageAlertId { get; private set; }
private static MetricAlert CreateRedisMemoryUsageAlert(ResourceGroup resourceGroup, Pulumi.AzureNative.Cache.Redis redis, ActionGroup actionGroup)
{
return new MetricAlert(name, new MetricAlertArgs
{
...
});
}
}
public sealed class AlertStack
{
public static IDictionary<string, object> Run()
{
var resources = new RedisAlerts();
var redisAlertStackReferences = GetStackReferences(resources.AllMetricAlerts.Values);
var metricAlerts = resources.AllMetricAlerts.ToDictionary(
p => p.Key,
p => CreateMetricAlert(p.Value, redisAlertStackReferences)
);
return metricAlerts.ToDictionary(
p => p.Key,
p => p.Value.Apply(a => a.Id) as object
);
}
private static DataCube2<string, string, Output<object>> GetStackReferences<TScope>(IEnumerable<MewsResource<MetricAlert<TScope>>> alerts)
where TScope : class
{
var resourceGroupDependencies = alerts.Select(a => a.Resource.Map(r => (StackName: r.ResourceGroup.StackName, OutputName: r.ResourceGroup.OutputName))).Flatten();
var scopeDependencies = alerts.Select(a => a.Resource.Map(r => (StackName: r.TargetResource.StackName, OutputName: r.TargetResource.OutputName))).Flatten();
var actionGroupDependencies = alerts.Select(a => a.Resource.Map(r => (StackName: r.ActionGroup.StackName, OutputName: r.ActionGroup.OutputName))).Flatten();
var dependencies = new[]
{
resourceGroupDependencies,
scopeDependencies,
actionGroupDependencies
}.Distinct();
return dependencies.ToDataCube(
t => t.StackName,
t => t.OutputName,
t => StackReferences.Get(t.StackName).GetOutput(t.OutputName)
);
}
}
public sealed class AzureDevelopmentStack : Pulumi.Stack
{
public AzureDevelopmentStack()
{
DeployResourceGroups();
DeployActionGroups();
DeployRedisCaches();
DeployRedisAlerts();
}
private static void DeployResourceGroups()
{
Deployer.Deploy<ResourceGroup, Pulumi.AzureNative.Resources.ResourceGroup>(ResourceGroups.All);
}
private static void DeployActionGroups()
{
Deployer.Deploy<ActionGroup, Pulumi.AzureNative.Insights.ActionGroup>(ActionGroups.All);
}
private static void DeployRedisCaches()
{
Deployer.Deploy<RedisCache, Pulumi.AzureNative.Cache.Redis>(RedisCaches.All);
}
private static void DeployRedisAlerts()
{
Deployer.Deploy<MetricAlert<RedisCache>, Pulumi.AzureNative.Insights.MetricAlert>(RedisAlerts.All);
}
}
public class MetricAlertDeployer<TResource, TPulumiResource> : ResourceDeployer<MetricAlert<TResource>, MetricAlert>
where TPulumiResource : Pulumi.CustomResource
where TResource : IResource
{
protected override MetricAlert Get(ManualResource alert)
{
return MetricAlert.Get(
name: alert.Name,
id: alert.Id.ToString()
);
}
protected override DeployResult<MetricAlert> Create(MetricAlert<TResource> alert, CustomResourceOptions options)
{
return ResourceDependencies.Resolve(
alert.ResourceGroup,
alert.ActionGroup,
alert.TargetResource,
(Pulumi.AzureNative.Resources.ResourceGroup resourceGroup, Pulumi.AzureNative.Insights.ActionGroup actionGroup, TPulumiResource targetResource) => Create(alert, resourceGroup, actionGroup, targetResource, options)
);
}
private static MetricAlert Create(MetricAlert<TResource> alert, Pulumi.AzureNative.Resources.ResourceGroup resourceGroup, Pulumi.AzureNative.Insights.ActionGroup actionGroup, TPulumiResource targetResource, CustomResourceOptions options)
{
return new MetricAlert(
name: alert.Name,
args: new MetricAlertArgs
{
RuleName = alert.Name,
ResourceGroupName = resourceGroup.Name,
Scopes = targetResource.Id,
...
},
options: options
);
}
}
public sealed class RedisAlerts
{
public readonly MewsResource<MetricAlert<RedisCache>> MemoryHigh = MewsResource<MetricAlert<RedisCache>>.CreateManaged(
name: "redis-memory-high",
resource: new MetricAlert<RedisCache>(
resourceGroup: ResourceDependency<ResourceGroup>.Create<ResourceGroups>(g => g.Monitoring),
actionGroup: ResourceDependency<ActionGroup>.Create<ActionGroups>(g => g.Default),
targetResource: ResourceDependency<RedisCache>.Create<RedisCaches>(c => c.WestEurope),
description: "Redis memory high"
)
);
public RedisAlerts()
{
AllMetricAlerts = new Dictionary<string, MewsResource<MetricAlert<RedisCache>>>
{
{ nameof(MemoryHigh), MemoryHigh }
};
}
public Dictionary<string, MewsResource<MetricAlert<RedisCache>>> AllMetricAlerts { get; }
}
public static class RedisAlerts
{
public static readonly Resource<MetricAlert<RedisCache>> MemoryHigh = Resource<MetricAlert<RedisCache>>.CreateImported(
id: AzureResourceId.Create(ResourceGroups.Monitoring, ResourceProviders.MetricAlert, "redis-memory-usage"),
resource: new MetricAlert<RedisCache>(
name: "redis-memory-usage",
resourceGroup: ResourceGroups.Monitoring,
targetResource: RedisCaches.WestEurope,
actionGroup: ActionGroups.Default,
...
)
);
static RedisAlerts()
{
All = new[]
{
MemoryHigh
};
}
public static IEnumerable<Resource<MetricAlert<RedisCache>>> All { get; }
}
public class ResourceDeployer<TResource, TPulumiResource>
where TResource : IResource
where TPulumiResource : Pulumi.CustomResource
{
public DeployResult<TPulumiResource> Deploy(Resource<TResource> resource)
{
return GetOrCreate(
resource,
r => r.Match(
manual => DeployResult.Create(Get(manual)),
imported => Create(imported.Resource, new CustomResourceOptions { Protect = true, ImportId = imported.Id.Value }),
managed => Create(managed.Resource, new CustomResourceOptions { Protect = true })
)
);
}
protected abstract TPulumiResource Get(ManualResource resource);
protected abstract DeployResult<TPulumiResource> Create(TResource resource, CustomResourceOptions options);ss
}
public sealed class ResourceGroups
{
public readonly MewsResource<ResourceGroup> Monitoring = MewsResource<ResourceGroup>.CreateManual(ResourceGroup.Monitoring.Id);
public AzureResourceGroups()
{
All = new Dictionary<string, MewsResource<ResourceGroup>>
{
{ nameof(Monitoring), Monitoring }
};
}
public Dictionary<string, MewsResource<ResourceGroup>> All { get; }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment