@@ -46,6 +46,11 @@ func (inst *Installer) getCluster(ctx context.Context, clusterConfig *ClusterCon
4646}
4747
4848func (inst * Installer ) createCluster (ctx context.Context , clusterConfig * ClusterConfig ) (* armcontainerservice.ManagedCluster , error ) {
49+ outboundIpAddress , err := inst .createOutboundIpAddress (ctx , clusterConfig )
50+ if err != nil {
51+ return nil , fmt .Errorf ("failed to create outbound IP address: %w" , err )
52+ }
53+
4954 var tags map [string ]* string
5055
5156 existingCluster , err := inst .getCluster (ctx , clusterConfig )
@@ -122,7 +127,9 @@ func (inst *Installer) createCluster(ctx context.Context, clusterConfig *Cluster
122127 Enabled : Ptr (true ),
123128 },
124129 },
125- NetworkProfile : & armcontainerservice.NetworkProfile {},
130+ NetworkProfile : & armcontainerservice.NetworkProfile {
131+ LoadBalancerProfile : & armcontainerservice.ManagedClusterLoadBalancerProfile {},
132+ },
126133 },
127134 SKU : & armcontainerservice.ManagedClusterSKU {
128135 Name : Ptr (armcontainerservice .ManagedClusterSKUNameBase ),
@@ -148,6 +155,12 @@ func (inst *Installer) createCluster(ctx context.Context, clusterConfig *Cluster
148155 }
149156 }
150157
158+ if outboundIpAddress != nil {
159+ cluster .Properties .NetworkProfile .LoadBalancerProfile .OutboundIPs = & armcontainerservice.ManagedClusterLoadBalancerProfileOutboundIPs {
160+ PublicIPs : []* armcontainerservice.ResourceReference {{ID : outboundIpAddress .ID }},
161+ }
162+ }
163+
151164 if workspace := inst .Config .Cloud .LogAnalyticsWorkspace ; workspace != nil {
152165 oic , err := armoperationalinsights .NewWorkspacesClient (inst .Config .Cloud .SubscriptionID , inst .Credential , nil )
153166 if err != nil {
@@ -459,9 +472,77 @@ func (inst *Installer) createCluster(ctx context.Context, clusterConfig *Cluster
459472 }
460473 }
461474
475+ if outboundIpAddress != nil {
476+ if err := assignRbacRole (ctx , []string {* createdCluster .Identity .PrincipalID }, false , * outboundIpAddress .ID , "Network Contributor" , inst .Config .Cloud .SubscriptionID , inst .Credential ); err != nil {
477+ return nil , fmt .Errorf ("failed to assign RBAC role on outbound IP address: %w" , err )
478+ }
479+ }
480+
462481 return & createdCluster , nil
463482}
464483
484+ func (inst * Installer ) createOutboundIpAddress (ctx context.Context , cluster * ClusterConfig ) (* armnetwork.PublicIPAddress , error ) {
485+ if len (cluster .OutboundIpServiceTags ) == 0 {
486+ // We let AKS manage the IP for us
487+ return nil , nil
488+ }
489+
490+ log .Ctx (ctx ).Info ().Msg ("Creating outbound IP address" )
491+ publicIPAddressesClient , err := armnetwork .NewPublicIPAddressesClient (inst .Config .Cloud .SubscriptionID , inst .Credential , nil )
492+ if err != nil {
493+ log .Fatal ().Err (err ).Msg ("failed to create public IP addresses client" )
494+ }
495+
496+ ipAddressName := fmt .Sprintf ("%s-outbound-ip" , cluster .Name )
497+
498+ var tags map [string ]* string
499+ if resp , err := publicIPAddressesClient .Get (ctx , inst .Config .Cloud .ResourceGroup , ipAddressName , nil ); err == nil {
500+ if existingTag , ok := resp .Tags [TagKey ]; ok {
501+ if * existingTag != inst .Config .EnvironmentName {
502+ return nil , fmt .Errorf ("public IP address '%s' is already in use by environment '%s'" , ipAddressName , * existingTag )
503+ }
504+ tags = resp .Tags
505+ }
506+ }
507+
508+ if tags == nil {
509+ tags = make (map [string ]* string )
510+ }
511+ tags [TagKey ] = & inst .Config .EnvironmentName
512+
513+ ipTags := []* armnetwork.IPTag {}
514+ for _ , t := range cluster .OutboundIpServiceTags {
515+ ipTags = append (ipTags , & armnetwork.IPTag {
516+ IPTagType : & t .Type ,
517+ Tag : & t .Tag ,
518+ })
519+ }
520+
521+ ipAddress := armnetwork.PublicIPAddress {
522+ Location : & cluster .Location ,
523+ SKU : & armnetwork.PublicIPAddressSKU {
524+ Name : Ptr (armnetwork .PublicIPAddressSKUNameStandard ),
525+ Tier : Ptr (armnetwork .PublicIPAddressSKUTierRegional ),
526+ },
527+ Properties : & armnetwork.PublicIPAddressPropertiesFormat {
528+ PublicIPAllocationMethod : Ptr (armnetwork .IPAllocationMethodStatic ),
529+ IPTags : ipTags ,
530+ },
531+ }
532+
533+ poller , err := publicIPAddressesClient .BeginCreateOrUpdate (ctx , inst .Config .Cloud .ResourceGroup , ipAddressName , ipAddress , nil )
534+ if err != nil {
535+ return nil , fmt .Errorf ("failed to create output IP address: %w" , err )
536+ }
537+
538+ res , err := poller .PollUntilDone (ctx , nil )
539+ if err != nil {
540+ return nil , fmt .Errorf ("failed to create output IP address: %w" , err )
541+ }
542+
543+ return & res .PublicIPAddress , nil
544+ }
545+
465546func clusterNeedsUpdating (cluster , existingCluster armcontainerservice.ManagedCluster ) (hasChanges bool , onlyScaleDown bool ) {
466547 if * existingCluster .Properties .ProvisioningState != "Succeeded" {
467548 return true , false
@@ -628,6 +709,25 @@ func clusterNeedsUpdating(cluster, existingCluster armcontainerservice.ManagedCl
628709 return true , false
629710 }
630711
712+ desiredOutputIpCount := 0
713+ if cluster .Properties .NetworkProfile .LoadBalancerProfile .OutboundIPs != nil {
714+ desiredOutputIpCount = len (cluster .Properties .NetworkProfile .LoadBalancerProfile .OutboundIPs .PublicIPs )
715+ }
716+
717+ existingOutputIpCount := 0
718+ if existingCluster .Properties .NetworkProfile .LoadBalancerProfile .OutboundIPs != nil {
719+ existingOutputIpCount = len (existingCluster .Properties .NetworkProfile .LoadBalancerProfile .OutboundIPs .PublicIPs )
720+ }
721+
722+ if desiredOutputIpCount != existingOutputIpCount ||
723+ (desiredOutputIpCount > 0 &&
724+ ! slices .EqualFunc (
725+ cluster .Properties .NetworkProfile .LoadBalancerProfile .OutboundIPs .PublicIPs ,
726+ existingCluster .Properties .NetworkProfile .LoadBalancerProfile .OutboundIPs .PublicIPs ,
727+ func (a , b * armcontainerservice.ResourceReference ) bool { return * a .ID == * b .ID })) {
728+ return true , false
729+ }
730+
631731 return hasChanges , onlyScaleDown
632732}
633733
0 commit comments