Skip to content

Commit dbf088a

Browse files
authored
New Resource: azurerm_network_manager_routing_rule (hashicorp#30439)
[FEATURE] * **New Resource**: `azurerm_network_manager_routing_rule`
1 parent 49091a1 commit dbf088a

File tree

4 files changed

+719
-0
lines changed

4 files changed

+719
-0
lines changed
Lines changed: 351 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,351 @@
1+
package network
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net"
7+
"regexp"
8+
"strings"
9+
"time"
10+
11+
"github.com/hashicorp/go-azure-helpers/lang/pointer"
12+
"github.com/hashicorp/go-azure-helpers/lang/response"
13+
"github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema"
14+
"github.com/hashicorp/go-azure-sdk/resource-manager/network/2025-01-01/routingrules"
15+
"github.com/hashicorp/terraform-provider-azurerm/internal/sdk"
16+
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
17+
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation"
18+
)
19+
20+
var (
21+
_ sdk.ResourceWithUpdate = ManagerRoutingRuleResource{}
22+
_ sdk.ResourceWithCustomizeDiff = ManagerRoutingRuleResource{}
23+
)
24+
25+
type ManagerRoutingRuleResource struct{}
26+
27+
func (ManagerRoutingRuleResource) IDValidationFunc() pluginsdk.SchemaValidateFunc {
28+
return routingrules.ValidateRuleID
29+
}
30+
31+
func (ManagerRoutingRuleResource) ResourceType() string {
32+
return "azurerm_network_manager_routing_rule"
33+
}
34+
35+
func (ManagerRoutingRuleResource) ModelObject() interface{} {
36+
return &ManagerRoutingRuleResourceModel{}
37+
}
38+
39+
type ManagerRoutingRuleResourceModel struct {
40+
Description string `tfschema:"description"`
41+
Destination []ManagerRoutingRuleDestination `tfschema:"destination"`
42+
Name string `tfschema:"name"`
43+
NextHop []ManagerRoutingRuleNextHop `tfschema:"next_hop"`
44+
RuleCollectionId string `tfschema:"rule_collection_id"`
45+
}
46+
47+
type ManagerRoutingRuleDestination struct {
48+
Address string `tfschema:"address"`
49+
Type string `tfschema:"type"`
50+
}
51+
52+
type ManagerRoutingRuleNextHop struct {
53+
Address string `tfschema:"address"`
54+
Type string `tfschema:"type"`
55+
}
56+
57+
func (ManagerRoutingRuleResource) Arguments() map[string]*pluginsdk.Schema {
58+
return map[string]*pluginsdk.Schema{
59+
"name": {
60+
Type: pluginsdk.TypeString,
61+
Required: true,
62+
ForceNew: true,
63+
ValidateFunc: validation.StringMatch(
64+
regexp.MustCompile(`^[a-zA-Z0-9\_\.\-]{1,64}$`),
65+
"`name` must be between 1 and 64 characters long and can only contain letters, numbers, underscores(_), periods(.), and hyphens(-).",
66+
),
67+
},
68+
69+
"rule_collection_id": commonschema.ResourceIDReferenceRequiredForceNew(&routingrules.RuleCollectionId{}),
70+
71+
"destination": {
72+
Type: pluginsdk.TypeList,
73+
Required: true,
74+
MaxItems: 1,
75+
Elem: &pluginsdk.Resource{
76+
Schema: map[string]*pluginsdk.Schema{
77+
"address": {
78+
Type: pluginsdk.TypeString,
79+
Required: true,
80+
ValidateFunc: validation.StringIsNotEmpty,
81+
},
82+
"type": {
83+
Type: pluginsdk.TypeString,
84+
Required: true,
85+
ValidateFunc: validation.StringInSlice(routingrules.PossibleValuesForRoutingRuleDestinationType(), false),
86+
},
87+
},
88+
},
89+
},
90+
91+
"next_hop": {
92+
Type: pluginsdk.TypeList,
93+
Required: true,
94+
MaxItems: 1,
95+
Elem: &pluginsdk.Resource{
96+
Schema: map[string]*pluginsdk.Schema{
97+
"type": {
98+
Type: pluginsdk.TypeString,
99+
Required: true,
100+
ValidateFunc: validation.StringInSlice(routingrules.PossibleValuesForRoutingRuleNextHopType(), false),
101+
},
102+
"address": {
103+
Type: pluginsdk.TypeString,
104+
Optional: true,
105+
ValidateFunc: validation.IsIPAddress,
106+
},
107+
},
108+
},
109+
},
110+
111+
"description": {
112+
Type: pluginsdk.TypeString,
113+
Optional: true,
114+
ValidateFunc: validation.StringIsNotEmpty,
115+
},
116+
}
117+
}
118+
119+
func (ManagerRoutingRuleResource) Attributes() map[string]*pluginsdk.Schema {
120+
return map[string]*pluginsdk.Schema{}
121+
}
122+
123+
func (r ManagerRoutingRuleResource) CustomizeDiff() sdk.ResourceFunc {
124+
return sdk.ResourceFunc{
125+
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
126+
var model ManagerRoutingRuleResourceModel
127+
if err := metadata.DecodeDiff(&model); err != nil {
128+
return fmt.Errorf("decoding: %+v", err)
129+
}
130+
131+
if len(model.Destination) > 0 {
132+
v := model.Destination[0]
133+
if strings.EqualFold(v.Type, string(routingrules.RoutingRuleDestinationTypeAddressPrefix)) {
134+
if _, _, err := net.ParseCIDR(v.Address); err != nil {
135+
return fmt.Errorf("expanding `destination`: `address` must be a valid CIDR when `type` is `AddressPrefix`: %+v", err)
136+
}
137+
}
138+
}
139+
140+
if len(model.NextHop) > 0 {
141+
v := model.NextHop[0]
142+
if strings.EqualFold(v.Type, string(routingrules.RoutingRuleNextHopTypeVirtualAppliance)) && v.Address == "" {
143+
return fmt.Errorf("expanding `next_hop`: `address` is required when `type` is `VirtualAppliance`")
144+
}
145+
}
146+
147+
return nil
148+
},
149+
Timeout: 30 * time.Minute,
150+
}
151+
}
152+
153+
func (r ManagerRoutingRuleResource) Create() sdk.ResourceFunc {
154+
return sdk.ResourceFunc{
155+
Timeout: 30 * time.Minute,
156+
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
157+
client := metadata.Client.Network.RoutingRules
158+
subscriptionId := metadata.Client.Account.SubscriptionId
159+
160+
var config ManagerRoutingRuleResourceModel
161+
if err := metadata.Decode(&config); err != nil {
162+
return fmt.Errorf("decoding: %+v", err)
163+
}
164+
165+
ruleCollectionId, err := routingrules.ParseRuleCollectionID(config.RuleCollectionId)
166+
if err != nil {
167+
return err
168+
}
169+
170+
id := routingrules.NewRuleID(subscriptionId, ruleCollectionId.ResourceGroupName, ruleCollectionId.NetworkManagerName, ruleCollectionId.RoutingConfigurationName, ruleCollectionId.RuleCollectionName, config.Name)
171+
172+
existing, err := client.Get(ctx, id)
173+
if err != nil && !response.WasNotFound(existing.HttpResponse) {
174+
return fmt.Errorf("checking for presence of existing %s: %+v", id, err)
175+
}
176+
if !response.WasNotFound(existing.HttpResponse) {
177+
return metadata.ResourceRequiresImport(r.ResourceType(), id)
178+
}
179+
180+
payload := routingrules.RoutingRule{
181+
Name: pointer.To(config.Name),
182+
Properties: &routingrules.RoutingRulePropertiesFormat{
183+
Description: pointer.To(config.Description),
184+
Destination: expandNetworkManagerRoutingRuleDestination(config.Destination),
185+
NextHop: expandNetworkManagerRoutingRuleNextHop(config.NextHop),
186+
},
187+
}
188+
189+
if _, err := client.CreateOrUpdate(ctx, id, payload); err != nil {
190+
return fmt.Errorf("creating %s: %+v", id, err)
191+
}
192+
193+
metadata.SetID(id)
194+
195+
return nil
196+
},
197+
}
198+
}
199+
200+
func (r ManagerRoutingRuleResource) Read() sdk.ResourceFunc {
201+
return sdk.ResourceFunc{
202+
Timeout: 5 * time.Minute,
203+
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
204+
client := metadata.Client.Network.RoutingRules
205+
206+
id, err := routingrules.ParseRuleID(metadata.ResourceData.Id())
207+
if err != nil {
208+
return err
209+
}
210+
211+
resp, err := client.Get(ctx, *id)
212+
if err != nil {
213+
if response.WasNotFound(resp.HttpResponse) {
214+
return metadata.MarkAsGone(id)
215+
}
216+
217+
return fmt.Errorf("retrieving %s: %+v", *id, err)
218+
}
219+
220+
schema := ManagerRoutingRuleResourceModel{
221+
Name: id.RuleName,
222+
RuleCollectionId: routingrules.NewRuleCollectionID(id.SubscriptionId, id.ResourceGroupName, id.NetworkManagerName, id.RoutingConfigurationName, id.RuleCollectionName).ID(),
223+
}
224+
225+
if model := resp.Model; model != nil {
226+
if props := model.Properties; props != nil {
227+
schema.Description = pointer.From(model.Properties.Description)
228+
schema.Destination = flattenNetworkManagerRoutingRuleDestination(props.Destination)
229+
schema.NextHop = flattenNetworkManagerRoutingRuleNextHop(props.NextHop)
230+
}
231+
}
232+
233+
return metadata.Encode(&schema)
234+
},
235+
}
236+
}
237+
238+
func (r ManagerRoutingRuleResource) Update() sdk.ResourceFunc {
239+
return sdk.ResourceFunc{
240+
Timeout: 30 * time.Minute,
241+
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
242+
client := metadata.Client.Network.RoutingRules
243+
244+
id, err := routingrules.ParseRuleID(metadata.ResourceData.Id())
245+
if err != nil {
246+
return err
247+
}
248+
249+
var model ManagerRoutingRuleResourceModel
250+
if err := metadata.Decode(&model); err != nil {
251+
return fmt.Errorf("decoding: %+v", err)
252+
}
253+
254+
resp, err := client.Get(ctx, *id)
255+
if err != nil {
256+
return fmt.Errorf("retrieving %s: %+v", *id, err)
257+
}
258+
259+
if resp.Model == nil {
260+
return fmt.Errorf("retrieving %s: `model` was nil", *id)
261+
}
262+
if resp.Model.Properties == nil {
263+
return fmt.Errorf("retrieving %s: `properties` was nil", *id)
264+
}
265+
266+
parameters := resp.Model
267+
268+
if metadata.ResourceData.HasChange("description") {
269+
parameters.Properties.Description = pointer.To(model.Description)
270+
}
271+
272+
if metadata.ResourceData.HasChange("destination") {
273+
parameters.Properties.Destination = expandNetworkManagerRoutingRuleDestination(model.Destination)
274+
}
275+
276+
if metadata.ResourceData.HasChange("next_hop") {
277+
parameters.Properties.NextHop = expandNetworkManagerRoutingRuleNextHop(model.NextHop)
278+
}
279+
280+
if _, err := client.CreateOrUpdate(ctx, *id, *parameters); err != nil {
281+
return fmt.Errorf("updating %s: %+v", *id, err)
282+
}
283+
return nil
284+
},
285+
}
286+
}
287+
288+
func (r ManagerRoutingRuleResource) Delete() sdk.ResourceFunc {
289+
return sdk.ResourceFunc{
290+
Timeout: 30 * time.Minute,
291+
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
292+
client := metadata.Client.Network.RoutingRules
293+
294+
id, err := routingrules.ParseRuleID(metadata.ResourceData.Id())
295+
if err != nil {
296+
return err
297+
}
298+
299+
if err := client.DeleteThenPoll(ctx, *id, routingrules.DeleteOperationOptions{
300+
Force: pointer.To(true),
301+
}); err != nil {
302+
return fmt.Errorf("deleting %s: %+v", *id, err)
303+
}
304+
305+
return nil
306+
},
307+
}
308+
}
309+
310+
func expandNetworkManagerRoutingRuleDestination(input []ManagerRoutingRuleDestination) routingrules.RoutingRuleRouteDestination {
311+
if len(input) == 0 {
312+
return routingrules.RoutingRuleRouteDestination{}
313+
}
314+
315+
v := input[0]
316+
return routingrules.RoutingRuleRouteDestination{
317+
DestinationAddress: v.Address,
318+
Type: routingrules.RoutingRuleDestinationType(v.Type),
319+
}
320+
}
321+
322+
func flattenNetworkManagerRoutingRuleDestination(input routingrules.RoutingRuleRouteDestination) []ManagerRoutingRuleDestination {
323+
return []ManagerRoutingRuleDestination{
324+
{
325+
Address: input.DestinationAddress,
326+
Type: string(input.Type),
327+
},
328+
}
329+
}
330+
331+
func expandNetworkManagerRoutingRuleNextHop(input []ManagerRoutingRuleNextHop) routingrules.RoutingRuleNextHop {
332+
if len(input) == 0 {
333+
return routingrules.RoutingRuleNextHop{}
334+
}
335+
336+
v := input[0]
337+
338+
return routingrules.RoutingRuleNextHop{
339+
NextHopAddress: pointer.To(v.Address),
340+
NextHopType: routingrules.RoutingRuleNextHopType(v.Type),
341+
}
342+
}
343+
344+
func flattenNetworkManagerRoutingRuleNextHop(input routingrules.RoutingRuleNextHop) []ManagerRoutingRuleNextHop {
345+
return []ManagerRoutingRuleNextHop{
346+
{
347+
Address: pointer.From(input.NextHopAddress),
348+
Type: string(input.NextHopType),
349+
},
350+
}
351+
}

0 commit comments

Comments
 (0)